【问题标题】:Threads and delegates — I don't fully understand their relations线程和委托——我不完全理解它们的关系
【发布时间】:2010-09-25 08:47:53
【问题描述】:

我写的代码有点像这样:

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

而且它有效(有时感觉就像有多个线程)。

但我没有使用任何委托。

  1. 没有委托的踏步是什么意思?
  2. 如果需要委托,请告诉我与委托的连接是什么以及如何建立的。

【问题讨论】:

    标签: c# multithreading delegates type-inference


    【解决方案1】:

    多线程非常复杂。您正在剪切和粘贴代码,甚至没有学习线程的最基本方面 - 如何启动线程。将 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);
    
    
        }
    
    }
    }
    

    【讨论】:

    • 首先感谢您的详细回答。我会花时间仔细学习它。关于复制和粘贴... 有时,一个在社区学习足球,然后在一家好俱乐部完善的足球运动员比在俱乐部长大的球员要好。换句话说,有时人们需要先感受某事才能理解它。无论如何......你帮助了我,我感谢你
    【解决方案2】:

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

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

    编译器从 lambda 表达式和 Thread 构造函数具有的不同重载中推断,您的意图是:

    • 创建ThreadStart 委托的实例。
    • 将其作为参数传递给接受ThreadStart 对象的Thread 的构造函数重载。

    您也可以使用匿名委托语法等效地编写此代码:

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

    【讨论】:

    • 谢谢,当你从聪明人那里复制代码时,美好的事情就会发生,但是这种“糖”对我隐藏了“正常”的方式......我没有声明代表,当然不明白它是如何工作的......所以你帮助我理解我的程序工作 - 因为它写得正确,但我不明白引擎盖下发生了什么......我真正需要的是一个不“糖”的例子" 用于'调用不带参数的函数'和'调用带参数的函数'
    • lambda () =&gt; createSomething(dt, start, finish) 等价于new delegate() { return createSomething(dt, start, finish); }。如果委托返回void,lambda 不应该是:() =&gt; {createSomething(dt, start, finish);}?另一件事是,如果在这里使用这种 lambda 是合法的。
    • @Maciej Hehl:1. 大括号和分号是可选的。 2. Lamba 表达式总是可以代替匿名委托。
    • 这不仅仅是可选的。第一个是表达式 lambda,第二个是语句 lambda。这是两种类型的 lambda,在某些情况下使用某些类型有一些限制。一个是语句 lambdas 不能用于创建表达式树,但我不记得是否还有其他的。也许声明 lambda 在这里是合法的。如果createSomething 返回 void,它们可能是等价的,但真的是这样吗?
    • 是的,ThreadStart 被声明为:public delegate void ThreadStart(); 这就是在这种情况下它是可选的。
    猜你喜欢
    • 1970-01-01
    • 2016-04-20
    • 2011-07-01
    • 2015-12-21
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 2011-06-06
    相关资源
    最近更新 更多