如何等待线程完成.NET?

Max*_*sky 167 c# multithreading

我以前从未在C#中使用线程,我需要有两个线程,以及主UI线程.基本上,我有以下几点.

public void StartTheActions()
{
  //Starting thread 1....
  Thread t1 = new Thread(new ThreadStart(action1));
  t1.Start();

  // Now, I want for the main thread (which is calling `StartTheActions` method) 
  // to wait for `t1` to finish. I've created an event in `action1` for this. 
  // The I wish `t2` to start...

  Thread t2 = new Thread(new ThreadStart(action2));
  t2.Start();
}
Run Code Online (Sandbox Code Playgroud)

所以,基本上,我的问题是如何让一个线程等待另一个线程完成.做这个的最好方式是什么?

Chr*_*s S 259

我可以看到5个选项:

1. Thread.Join

和米奇的回答一样.但这会阻止你的UI线程,但是你会为你内置一个Timeout.


2.使用a WaitHandle

ManualResetEventWaitHandlejrista建议的.

需要注意的一件事是,如果你想等待多个线程,WaitHandle.WaitAll()默认情况下不会工作,因为它需要一个MTA线程.您可以通过标记您的Main()方法来解决这个问题MTAThread- 但这会阻止您的消息泵,并且不建议从我读过的内容.


3.开火活动

这个页面乔恩斯基特有关事件和多线程,它可能是一个事件能够成为之间unsubcribed ifEventName(this,EventArgs.Empty)-它之前发生在我身上.

(希望这些编译,我还没试过)

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();
        worker.ThreadDone += HandleThreadDone;

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone(object sender, EventArgs e)
    {
        // You should get the idea this is just an example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();
            worker.ThreadDone += HandleThreadDone;

            Thread thread2 = new Thread(worker.Run);
            thread2.Start();

            _count++;
        }
    }

    class ThreadWorker
    {
        public event EventHandler ThreadDone;

        public void Run()
        {
            // Do a task

            if (ThreadDone != null)
                ThreadDone(this, EventArgs.Empty);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

4.使用代表

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();

        Thread thread1 = new Thread(worker.Run);
        thread1.Start(HandleThreadDone);

        _count = 1;
    }

    void HandleThreadDone()
    {
        // As before - just a simple example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();

            Thread thread2 = new Thread(worker.Run);
            thread2.Start(HandleThreadDone);

            _count++;
        }
    }

    class ThreadWorker
    {
        // Switch to your favourite Action<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你确实使用了_count方法,那么使用它来增加它可能是一个想法(为了安全起见)

Interlocked.Increment(ref _count)

我有兴趣知道使用委托和事件进行线程通知之间的区别,我知道的唯一区别是事件被同步调用.


5.改为异步

这个问题的答案用这种方法非常清楚地描述了你的选择.


在错误的线程上委托/事件

事件/委托的处理方式意味着您的事件处理程序方法在thread1/thread2上而不是主UI线程,因此您需要在HandleThreadDone方法的顶部切换回来:

// Delegate example
if (InvokeRequired)
{
    Invoke(new Action(HandleThreadDone));
    return;
}
Run Code Online (Sandbox Code Playgroud)


Mit*_*eat 56

t1.Join();    // Wait until thread t1 finishes
Run Code Online (Sandbox Code Playgroud)

在你启动它之后,但这不会有太大的成就,因为它与在主线程上运行的结果相同!

如果你想了解.NET中的线程,我强烈建议阅读Joe Albahari 在C#免费电子书中的线程.

  • 我想清楚地说明,Join是一种工具,尽管它经常被误用,但它可能很有用.在某些情况下,它只会在没有任何不必要的副作用的情况下工作(例如在很长一段时间内停止主GUI线程). (5认同)
  • 尽管`Join`确实是提问者似乎一直要求的,但总的来说这可能非常糟糕.对"Join"的调用将挂起执行此操作的线程.如果这恰好是主要的GUI线程,那就是*BAD*!作为用户,我主动厌恶似乎以这种方式工作的应用程序.所以请看这个问题的所有其他答案和http://stackoverflow.com/questions/1221374/how-can-i-improve-the-subjective-speed-of-my-application/1221420#1221420 (4认同)
  • 伙计们,*一种尺寸不适合所有*.有些情况,当一个人确实需要确定时,该线程已完成其工作:考虑,线程处理即将被更改的数据.在这种情况下通知线程优雅地取消并等到它完成(特别是当一个步骤被非常快速地处理)时,IMO是完全合理的.我宁愿说,Join是*evil*(用C++ FAQ术语),即.除非**确实**要求,否则不得使用. (2认同)
  • 加入是工具吗?我想您会发现这是一种方法。 (2认同)

jri*_*sta 32

前两个答案很棒,适用于简单的场景.但是,还有其他方法可以同步线程.以下内容也适用:

public void StartTheActions()
{
    ManualResetEvent syncEvent = new ManualResetEvent(false);

    Thread t1 = new Thread(
        () =>
        {
            // Do some work...
            syncEvent.Set();
        }
    );
    t1.Start();

    Thread t2 = new Thread(
        () =>
        {
            syncEvent.WaitOne();

            // Do some work...
        }
    );
    t2.Start();
}
Run Code Online (Sandbox Code Playgroud)

ManualResetEvent是.NET框架必须提供的各种WaitHandle之一.它们可以提供比简单但非常常见的工具(如lock()/ Monitor,Thread.Join等)更丰富的线程同步功能.它们还可以用于同步两个以上的线程,允许复杂的场景,如"主"线程协调多个"子"线程,多个并发进程依赖于彼此的几个阶段进行同步等.


Say*_*emi 24

如果从.NET 4使用,此示例可以帮助您:

class Program
{
    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => doStuff());
        Task task2 = Task.Factory.StartNew(() => doStuff());
        Task task3 = Task.Factory.StartNew(() => doStuff());
        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("All threads complete");
    }

    static void doStuff()
    {
        //do stuff here
    }
}
Run Code Online (Sandbox Code Playgroud)

来自:https://stackoverflow.com/a/4190969/1676736

  • 你在答案中没有提到有关Threads的任何内容.问题是关于线程,而不是任务.两者不一样. (7认同)
  • 我带来了与原始海报相似的问题(和知识水平),这个答案对我来说非常有价值 - 任务更适合我正在做的事情,如果我没有找到这个答案我会写下我的自己可怕的线程池. (4认同)
  • 它可能与原始问题无关,因为它使用任务,但它完成完全相同的事情。OP 表示他们从未在 C# 中做过线程等,可能只是询问线程,因为他不知道任务的存在。这简单、优雅,而且很有魅力。附带说明一下,如果您要启动 x 数量的任务,您可以在一系列任务上 WaitAll。 (2认同)

Dan*_*den 5

您需要该Thread.Join()方法或其中一个重载.