Подтвердить что ты не робот

Темы и делегаты - я не полностью понимаю их отношения

Я написал код, который выглядит примерно так:

Thread t = new Thread(() => createSomething(dt, start, finish) );
t.Start();

и он работает (иногда почти кажется, что существует несколько потоков)

пока я не использую делегатов.

  • В чем смысл протектора без делегата
  • Если требуется делегат, то, пожалуйста, скажите мне, что и как делается с делегатом.
4b9b3361

Ответ 1

Многопоточность очень сложна. Вы разрезаете и вставляете код, даже не узнавая ничего о самых элементарных аспектах потоковой передачи - как начать поток. Вставка чего-то из Интернета в пользовательский интерфейс для исправления или настройки элемента управления - это одно. Это совершенно другой вид процесса. Вам нужно изучить тему, написать весь свой собственный код и точно понять, как это работает, иначе вы просто тратите свое время на это.

Делегат - это .NET-версия указателя безопасной функции типа. Для всех потоков требуется точка входа, чтобы начать выполнение. По определению, когда создается первичный поток, он всегда запускает Main() в качестве точки входа. Любые дополнительные потоки, которые вы создаете, должны иметь явно определенную точку входа - указатель на функцию, в которой они должны начать выполнение. Поэтому потоки всегда требуют делегата.

Делегаты часто используются для потоковой передачи для других целей, в основном для обратных вызовов. Если вы хотите, чтобы поток сообщал о какой-либо информации, такой как статус завершения, одна из возможностей - создать функцию обратного вызова, которую может использовать поток. Снова поток нуждается в указателе, чтобы иметь возможность выполнить обратный вызов, поэтому делегаты также используются для этого. В отличие от точки входа, это необязательно, но концепция одинаков.

Связь между потоками и делегатами - это вторичные потоки, которые не могут просто вызывать методы, подобные первому потоку приложения, поэтому вместо этого требуется указатель на функции, а делегаты действуют как указатели на функции.

Вы не видите делегата, и вы его не создали, потому что инфраструктура делает это для вас в конструкторе Thread. Вы можете передать метод, который хотите использовать для запуска потока, а код рамки создает делегат, который указывает на этот метод для вас. Если вы хотите использовать обратный вызов, вам нужно будет создать делегат самостоятельно.

Вот код без лямбда-выражений. SomeClass имеет некоторую обработку, которая занимает много времени и выполняется по фоновым потокам. Чтобы помочь в этом, был создан SomeThreadTask, и он содержит код процесса и все, что нужно для запуска потока. Второй делегат используется для обратного вызова, когда поток выполняется.

Реальный код будет более сложным, и реальный класс никогда не должен знать, как создавать потоки и т.д., поэтому у вас будут объекты менеджера.

// Create a delegate for our callback function.
public delegate void SomeThreadTaskCompleted(string taskId, bool isError);


public class SomeClass
{

    private void DoBackgroundWork()
    {
        // Create a ThreadTask object.

        SomeThreadTask threadTask = new SomeThreadTask();

        // Create a task id.  Quick and dirty here to keep it simple.  
        // Read about threading and task identifiers to learn 
        // various ways people commonly do this for production code.

        threadTask.TaskId = "MyTask" + DateTime.Now.Ticks.ToString();

        // Set the thread up with a callback function pointer.

        threadTask.CompletedCallback = 
            new SomeThreadTaskCompleted(SomeThreadTaskCompletedCallback);


        // Create a thread.  We only need to specify the entry point function.
        // Framework creates the actual delegate for thread with this entry point.

        Thread thread = new Thread(threadTask.ExecuteThreadTask);

        // Do something with our thread and threadTask object instances just created
        // so we could cancel the thread etc.  Can be as simple as stick 'em in a bag
        // or may need a complex manager, just depends.

        // GO!
        thread.Start();

        // Go do something else.  When task finishes we will get a callback.

    }

    /// <summary>
    /// Method that receives callbacks from threads upon completion.
    /// </summary>
    /// <param name="taskId"></param>
    /// <param name="isError"></param>
    public void SomeThreadTaskCompletedCallback(string taskId, bool isError)
    {
        // Do post background work here.
        // Cleanup the thread and task object references, etc.
    }
}


/// <summary>
/// ThreadTask defines the work a thread needs to do and also provides any data 
/// required along with callback pointers etc.
/// Populate a new ThreadTask instance with any data the thread needs 
/// then start the thread to execute the task.
/// </summary>
internal class SomeThreadTask
{

    private string _taskId;
    private SomeThreadTaskCompleted _completedCallback;

    /// <summary>
    /// Get. Set simple identifier that allows main thread to identify this task.
    /// </summary>
    internal string TaskId
    {
        get { return _taskId; }
        set { _taskId = value; }
    }

    /// <summary>
    /// Get, Set instance of a delegate used to notify the main thread when done.
    /// </summary>
    internal SomeThreadTaskCompleted CompletedCallback
    {
        get { return _completedCallback; }
        set { _completedCallback = value; }
    }

    /// <summary>
    /// Thread entry point function.
    /// </summary>
    internal void ExecuteThreadTask()
    {
        // Often a good idea to tell the main thread if there was an error
        bool isError = false;

        // Thread begins execution here.

        // You would start some kind of long task here 
        // such as image processing, file parsing, complex query, etc.

        // Thread execution eventually returns to this function when complete.

        // Execute callback to tell main thread this task is done.
        _completedCallback.Invoke(_taskId, isError);


    }

}
}

Ответ 2

Вы используете делегат - это просто синтаксический сахар С# для:

Thread t = new Thread(new ThreadStart( () => createSomething(dt, start, finish))); 
t.Start();

Компилятор выводит из выражения лямбда и разных перегрузок, которые имеет конструктор Thread, что вы намерены:

  • Создайте экземпляр делегата ThreadStart.
  • Передайте его как аргумент перегрузки конструктора Thread, который принимает объект ThreadStart.

Вы также можете записать это с помощью синтаксиса анонимного делегата:

 Thread t = new Thread(delegate() { createSomething(dt, start, finish); } ); 
 t.Start();

Если аргументы createSomething не являются (захваченными) локалями, вы можете написать это без анонимных методов вообще, что должно более четко выделить создание делегата:

private void Create()
{
   createSomething(dt, start, finish))); 
}

...

Thread t = new Thread(new ThreadStart(Create)); //new ThreadStart is optional for the same reason 
t.Start();