线程和代表 - 我不完全理解他们的关系

时间:2010-09-25 08:47:54

标签: c# multithreading delegates type-inference

我写了一段看起来像这样的代码:

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

它有效(有时几乎感觉有多个线程)。

但我不使用任何代表。

  1. 没有代表的踩踏是什么意思?
  2. 如果有必要代表 - 那么请告诉我与代表的联系是什么以及如何建立。

2 个答案:

答案 0 :(得分:39)

多线程非常复杂。你正在剪切和粘贴代码,甚至没有学习任何关于线程的最基本方面 - 如何启动线程。将Web上的内容粘贴到UI中以修复或调整控件是一回事。这是一个完全不同的过程。你需要研究这个主题,编写你自己的代码,并准确理解它是如何工作的,否则你只是在浪费时间。

委托是类型安全函数指针的.NET版本。所有线程都需要一个入口点才能开始执行。根据定义,当创建主线程时,它总是运行Main()作为其入口点。您创建的任何其他线程都需要一个显式定义的入口点 - 指向它们应该开始执行的函数的指针。所以线程总是需要一个委托。

代理通常也用于其他目的的线程,主要是回调。如果您希望线程报告某些信息(例如完成状态),则可以创建线程可以使用的回调函数。线程再次需要一个能够执行回调的指针,因此也可以使用委托。与入口点不同,这些是可选的,但概念是相同的。

线程和委托之间的关系是辅助线程不能只调用主应用程序线程之类的方法,因此需要一个函数指针,而委托充当函数指针。

您没有看到委托,也没有创建委托,因为框架是在Thread构造函数中为您完成的。您可以传入要用于启动线程的方法,框架代码会创建一个指向此方法的委托。如果你想使用回调,你必须自己创建一个委托。

这是没有lambda表达式的代码。 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);


    }

}
}

答案 1 :(得分:25)

使用委托 - 这只是C#语法糖:

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

编译器从lambda表达式推断以及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();