20 Stimmen

Threads und Delegierte - ich verstehe ihre Beziehungen nicht ganz

Ich habe einen Code geschrieben, der in etwa wie folgt aussieht:

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

Und es funktioniert (manchmal fühlt es sich fast so an, als gäbe es mehrere Threads).

Dennoch verwende ich keine Delegierten.

  1. Was bedeutet ein Profil ohne Delegierten?
  2. Wenn ein Delegierter notwendig ist - dann sagen Sie mir bitte, was und wie die Verbindung zum Delegierten hergestellt wird.

47voto

Sisyphus Punkte 4073

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);

    }

}
}

6 Stimmen

Zunächst einmal danke ich Ihnen für Ihre ausführliche Antwort. Ich werde mir die Zeit nehmen, sie sorgfältig zu lesen. Was das Kopieren und Einfügen angeht... Manchmal ist ein Fußballspieler, der das Fußballspielen in der Nachbarschaft erlernt und dann in einem guten Verein perfektioniert, besser als einer, der in einem Verein aufgewachsen ist. Mit anderen Worten: Manchmal muss man etwas erst fühlen, bevor man es versteht. Wie auch immer... Sie haben mir geholfen und ich danke Ihnen dafür.

28voto

Ani Punkte 107342

Sie sind mit einem Delegaten - dies ist nur C# syntaktischen Zucker für:

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

Der Compiler ist Ableitung aus dem Lambda-Ausdruck und den verschiedenen Überladungen, die der Thread Konstruktor hat, dass Ihre Absicht ist,:

  • Erstellen Sie eine Instanz der ThreadStart delegieren.
  • Übergeben Sie es als Argument an die Konstruktorüberladung von Thread die eine ThreadStart Objekt.

Sie könnten dies auch mit der Syntax für anonyme Delegierte schreiben:

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

Wenn die Argumente für createSomething keine (gefangenen) lokalen Methoden sind, könnten Sie dies ganz ohne anonyme Methoden schreiben, was die Erstellung des Delegaten viel deutlicher hervorheben sollte:

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

...

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

0 Stimmen

Danke, schöne Dinge passieren, wenn man Code von klugen Leuten kopiert, aber dieser "Zucker" verbirgt den "normalen" Weg vor mir... Ich deklariere keinen Delegaten und verstehe sicher nicht, wie er funktioniert... Sie haben mir also geholfen zu verstehen, dass mein Programm funktioniert - weil es korrekt geschrieben ist, aber ich verstehe nicht, was unter der Haube passiert... was ich wirklich brauche, ist ein Toturial \example für nicht "Zucker" für "Aufruf einer Funktion ohne Parameter" und "Aufruf einer Funktion mit Parametern

1 Stimmen

Das Lambda () => createSomething(dt, start, finish) ist gleichbedeutend mit new delegate() { return createSomething(dt, start, finish); } . Wenn der Delegierte zurückkehrt void sollte das Lambda nicht so aussehen: () => {createSomething(dt, start, finish);} ? Eine andere Frage ist, ob die Verwendung eines solchen Lambdas hier legal ist.

0 Stimmen

@Maciej Hehl: 1. Die geschweiften Klammern und das Semikolon sind optional. 2. Lamba-Ausdrücke können anonyme Delegierte immer ersetzen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X