C# 如何等待线程完成.NET?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1584062/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wait for thread to finish with .NET?
提问by Maxim Zaslavsky
I've never really used threading before in C# where I need to have two threads, as well as the main UI thread. Basically, I have the following.
我以前从未在 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();
}
So, essentially, my question is how to have a thread wait for another one to finish. What is the best way to do this?
所以,本质上,我的问题是如何让一个线程等待另一个线程完成。做这个的最好方式是什么?
采纳答案by Chris S
I can see 5 options available:
我可以看到 5 个可用选项:
1. Thread.Join
1.线程连接
As with Mitch's answer. But this will block your UI thread, however you get a Timeout built in for you.
就像米奇的回答一样。但这会阻塞你的 UI 线程,但是你会为你内置一个超时。
2. Use a WaitHandle
2. 使用 WaitHandle
ManualResetEvent
is a WaitHandle
as jrista suggested.
ManualResetEvent
是WaitHandle
jrista 建议的。
One thing to note is if you want to wait for multiple threads, WaitHandle.WaitAll()
won't work by default, as it needs an MTA thread. You can get around this by marking your Main()
method with MTAThread
- however this blocks your message pump and isn't recommended from what I've read.
需要注意的一件事是,如果您想等待多个线程,WaitHandle.WaitAll()
默认情况下不会工作,因为它需要一个 MTA 线程。你可以通过标记你的Main()
方法来解决这个问题MTAThread
- 但是这会阻止你的消息泵,我读过的内容不推荐。
3. Fire an event
3. 触发事件
See this page by Jon Skeetabout events and multi-threading, it's possible that an event can become unsubcribed between the if
and the EventName(this,EventArgs.Empty)
- it's happened to me before.
请参阅Jon Skeet 撰写的有关事件和多线程的页面,事件可能会在if
和之间取消订阅EventName(this,EventArgs.Empty)
- 这在我身上发生过。
(Hopefully these compile, I haven't tried)
(希望这些编译通过,我没试过)
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);
}
}
}
4. Use a delegate
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();
}
}
}
If you do use the _count method, it might be an idea (to be safe) to increment it using
如果您确实使用了 _count 方法,那么使用增加它可能是一个想法(为了安全)
Interlocked.Increment(ref _count)
Interlocked.Increment(ref _count)
I'd be interested to know the difference between using delegates and events for thread notification, the only difference I know are events are called synchronously.
我很想知道使用委托和事件进行线程通知之间的区别,我知道的唯一区别是同步调用事件。
5. Do it asynchronously instead
5.改为异步执行
The answer to this questionhas a very clear description of your options with this method.
这个问题的答案非常清楚地描述了您使用此方法的选项。
Delegate/Events on the wrong thread
错误线程上的委托/事件
The event/delegate way of doing things will mean your event handler methodis on thread1/thread2 not the main UI thread, so you will need to switch back right at the top of the HandleThreadDone methods:
事件/委托的处理方式意味着您的事件处理程序方法在 thread1/thread2 上而不是主 UI 线程上,因此您需要在 HandleThreadDone 方法的顶部切换回来:
// Delegate example
if (InvokeRequired)
{
Invoke(new Action(HandleThreadDone));
return;
}
回答by Mitch Wheat
Add
添加
t1.Join(); // Wait until thread t1 finishes
after you start it, but that won't accomplish much as it's essentialy the same result as running on the main thread!
启动它后,但这不会完成太多,因为它本质上与在主线程上运行的结果相同!
I can highly recommended reading Joe Albahari's Threading in C#free e-book, if you want to gain an understanding of threading in .NET.
如果您想了解 .NET 中的线程,我强烈建议您阅读 Joe Albahari 的C# 线程中的免费电子书。
回答by Daniel Pryden
You want the Thread.Join()
method, or one of its overloads.
您需要该Thread.Join()
方法或其重载之一。
回答by jrista
The previous two answers are great, and will work for simple scenarios. There are other ways to synchronize threads, however. The following will also work:
前两个答案很好,适用于简单的场景。但是,还有其他方法可以同步线程。以下也将起作用:
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();
}
ManualResetEventis one of the various WaitHandle's that the .NET framework has to offer. They can provide much richer thread synchronization capabilities than the simple but very common tools like lock()/Monitor, Thread.Join, etc. They can also be used to synchronize more than two threads, allowing complex scenarios such as a 'master' thread that coordinates multiple 'child' threads, multiple concurrent processes that are dependent upon several stages of each other to be synchronized, etc.
ManualResetEvent是.NET 框架必须提供的各种WaitHandle之一。它们可以提供比 lock()/Monitor、Thread.Join 等简单但非常常用的工具更丰富的线程同步功能。它们还可以用于同步两个以上的线程,允许复杂的场景,例如“主”线程协调多个“子”线程,多个并发进程,这些进程依赖于彼此的几个阶段进行同步,等等。
回答by Ed Power
I would have your main thread pass a callback method to your first thread, and when it's done, it will invoke the callback method on the mainthread, which can launch the second thread. This keeps your main thread from hanging while its waiting for a Join or Waithandle. Passing methods as delegates is a useful thing to learn with C# anyway.
我会让你的主线程向你的第一个线程传递一个回调方法,当它完成时,它会调用主线程上的回调方法,它可以启动第二个线程。这可以防止您的主线程在等待 Join 或 Waithandle 时挂起。无论如何,将方法作为委托传递是学习 C# 有用的事情。
回答by Adam
Posting to maybe help some others, spent quite a bit of time looking for a solution like what I came up with. So I took a little different approach. There is a counter option above, I just applied it a bit differently. I was spinning off numerous threads and incremented a counter and decremented a counter as a thread started and stopped. Then in the main method I was wanting to pause and wait for threads to complete I did.
发帖是为了帮助其他人,花了很多时间寻找像我想出的那样的解决方案。所以我采取了一些不同的方法。上面有一个计数器选项,我只是应用了一点不同。我正在旋转多个线程并在线程启动和停止时增加一个计数器并减少一个计数器。然后在 main 方法中,我想暂停并等待线程完成。
while (threadCounter > 0)
{
Thread.Sleep(500); //Make it pause for half second so that we don't spin the cpu out of control.
}
Documented on my blog. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/
记录在我的博客上。 http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/
回答by Wendell Tagsip
Try this:
尝试这个:
List<Thread> myThreads = new List<Thread>();
foreach (Thread curThread in myThreads)
{
curThread.Start();
}
foreach (Thread curThread in myThreads)
{
curThread.Join();
}
回答by Sayed Abolfazl Fatemi
If using from .NET 4 this sample can help you:
如果从 .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
}
}
回答by Mark Emerson
When I want the UI to be able to update its display while waiting for a task to complete, I use a while-loop that tests IsAlive on the thread:
当我希望 UI 能够在等待任务完成时更新其显示时,我使用了一个在线程上测试 IsAlive 的 while 循环:
Thread t = new Thread(() => someMethod(parameters));
t.Start();
while (t.IsAlive)
{
Thread.Sleep(500);
Application.DoEvents();
}
回答by user3029478
Here's a simple example that waits for a tread to finish, within the same class. It also makes a call to another class in the same namespace. I included the "using" statements so it can execute as a Winform as long as you create button1.
这是一个简单的示例,它在同一个类中等待胎面完成。它还调用同一命名空间中的另一个类。我包含了“using”语句,因此只要您创建 button1,它就可以作为 Winform 执行。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace ClassCrossCall
{
public partial class Form1 : Form
{
int number = 0; // this is an intentional problem, included for demonstration purposes
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
button1.Text="Initialized";
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text="Clicked";
button1.Refresh();
Thread.Sleep(400);
List<Task> taskList = new List<Task>();
taskList.Add(Task.Factory.StartNew(() => update_thread(2000)));
taskList.Add(Task.Factory.StartNew(() => update_thread(4000)));
Task.WaitAll(taskList.ToArray());
worker.update_button(this,number);
}
public void update_thread(int ms)
{
// It's important to check the scope of all variables
number=ms; // this could be either 2000 or 4000. Race condition.
Thread.Sleep(ms);
}
}
class worker
{
public static void update_button(Form1 form, int number)
{
form.button1.Text=$"{number}";
}
}
}