web-dev-qa-db-ja.com

スレッドとデリゲート—私はそれらの関係を完全には理解していません

私は次のようなコードを書きました。

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

そしてそれは機能します(時には複数のスレッドがあるように感じることもあります)。

しかし、私はデリゲートを使用していません。

  1. デリゲートのないトレッドの意味は何ですか?
  2. デリゲートが必要な場合は、デリゲートへの接続の内容と方法を教えてください。
16
Asaf

マルチスレッドは非常に複雑です。スレッドの最も基本的な側面、つまりスレッドの開始方法について何も学ばなくても、コードを切り取って貼り付けることができます。コントロールを修正または微調整するために、WebからUIに何かを貼り付けることは1つのことです。これはまったく異なる種類のプロセスです。あなたは主題を研究し、あなた自身のコードをすべて書き、そしてそれがどのように機能するかを正確に理解する必要があります、さもなければあなたはこれであなたの時間を無駄にしているだけです。

デリゲートは、タイプセーフな関数ポインタの.NETバージョンです。すべてのスレッドは、実行を開始するためにエントリポイントを必要とします。定義上、プライマリスレッドが作成されると、エントリポイントとして常にMain()が実行されます。作成する追加のスレッドには、明示的に定義されたエントリポイント(実行を開始する関数へのポインタ)が必要です。したがって、スレッドには常にデリゲートが必要です。

デリゲートは、他の目的、主にコールバックのスレッド化でもよく使用されます。スレッドが完了ステータスなどの情報を報告するようにしたい場合、1つの可能性は、スレッドが使用できるコールバック関数を作成することです。この場合も、スレッドはコールバックを実行できるようにポインターを必要とするため、デリゲートもこれに使用されます。エントリポイントとは異なり、これらはオプションですが、概念は同じです。

スレッドとデリゲートの関係は、セカンダリスレッドがプライマリアプリスレッドのようなメソッドを呼び出すことはできないため、代わりに関数ポインターが必要であり、デリゲートは関数ポインターとして機能します。

フレームワークがスレッドコンストラクターでデリゲートを実行しているため、デリゲートは表示されず、作成しませんでした。スレッドの開始に使用するメソッドを渡すことができ、フレームワークコードは、このメソッドを指すデリゲートを作成します。コールバックを使用する場合は、自分でデリゲートを作成する必要があります。

これがラムダ式のないコードです。 SomeClassには、時間がかかり、バックグラウンドスレッドで実行される処理があります。これを支援するために、SomeThreadTaskが作成されており、プロセスコードと、スレッドがそれを実行するために必要なすべてのものが含まれています。スレッドが完了すると、2番目のデリゲートがコールバックに使用されます。

実際のコードはより複雑になり、実際のクラスはスレッドの作成方法などを知る必要がないため、マネージャーオブジェクトが作成されます。

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


    }

}
}
41
Sisyphus

あなたareデリゲートを使用しています-これは次のC#構文糖衣です:

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

コンパイラーは推論ラムダ式とThreadコンストラクターが持つさまざまなオーバーロードから、次のことを目的としています。

  • ThreadStartデリゲートのインスタンスを作成します。
  • Threadオブジェクトを受け入れるThreadStartのコンストラクターオーバーロードに引数として渡します。

これをanonymous-delegate構文で同等に記述することもできます。

 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();
25
Ani