Multi-threading ist sehr komplex. Du schneidest Code aus und fügst ihn ein, ohne überhaupt etwas über die grundlegendsten Aspekte des Threading zu lernen - wie man einen Thread startet. Etwas aus dem Web in eine Benutzeroberfläche einzufügen, um ein Steuerelement zu reparieren oder zu optimieren, ist eine Sache. Dies ist ein völlig anderer Prozess. Sie müssen das Thema studieren, Ihren eigenen Code schreiben und genau verstehen, wie es funktioniert, sonst verschwenden Sie damit nur Ihre Zeit.
Ein Delegat ist die .NET-Version eines typsicheren Funktionszeigers. Alle Threads benötigen einen Einstiegspunkt, um die Ausführung zu starten. Wenn ein primärer Thread erstellt wird, wird per Definition immer Main() als Einstiegspunkt ausgeführt. Alle zusätzlichen Threads, die Sie erstellen, benötigen einen explizit definierten Einstiegspunkt - einen Zeiger auf die Funktion, mit der sie ihre Ausführung beginnen sollen. Threads benötigen also immer einen Delegaten.
Delegierte werden im Threading oft auch für andere Zwecke verwendet, hauptsächlich für Rückrufe. Wenn Sie möchten, dass ein Thread einige Informationen zurückmeldet, z. B. den Fertigstellungsstatus, besteht eine Möglichkeit darin, eine Callback-Funktion zu erstellen, die der Thread verwenden kann. Auch hier benötigt der Thread einen Zeiger, um den Callback ausführen zu können, so dass auch hierfür Delegates verwendet werden. Im Gegensatz zu einem Einstiegspunkt sind diese optional, aber das Konzept ist dasselbe.
Die Beziehung zwischen Threads und Delegaten besteht darin, dass sekundäre Threads nicht einfach Methoden wie der primäre App-Thread aufrufen können, so dass stattdessen ein Funktionszeiger erforderlich ist und Delegaten als Funktionszeiger fungieren.
Sie sehen den Delegaten nicht, und Sie haben keinen erstellt, weil das Framework dies im Thread-Konstruktor für Sie erledigt. Sie können die Methode übergeben, die Sie zum Starten des Threads verwenden möchten, und der Framework-Code erstellt einen Delegaten, der auf diese Methode verweist. Wenn Sie einen Rückruf verwenden wollten, müssten Sie selbst einen Delegaten erstellen.
Hier ist der Code ohne Lambda-Ausdrücke. SomeClass hat einige Verarbeitungen, die viel Zeit in Anspruch nehmen und in Hintergrund-Threads ausgeführt werden. Um dies zu unterstützen, wurde die SomeThreadTask erstellt, die den Prozesscode und alles, was der Thread zur Ausführung benötigt, enthält. Ein zweiter Delegat wird für einen Callback verwendet, wenn der Thread fertig ist.
Echter Code wäre komplizierter, und eine echte Klasse sollte nie wissen müssen, wie man Threads usw. erstellt, so dass man Managerobjekte hätte.
// 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);
}
}
}