C#多线程 - 出了啥问题,如何使用 AutoResetEvent
Posted
技术标签:
【中文标题】C#多线程 - 出了啥问题,如何使用 AutoResetEvent【英文标题】:C# Multi-Threading - What's wrong, how to use AutoResetEventC#多线程 - 出了什么问题,如何使用 AutoResetEvent 【发布时间】:2020-07-29 00:58:38 【问题描述】:我还在学习线程,但下面的代码有问题。抱歉,如果这个问题之前出现过,我只是不明白为什么这段代码不起作用。 我简化了代码:
static EventWaitHandle waitH; // AutoResetEvent, wait for signal
static bool whExit; // signal to exit waiting
static Queue<string> str; // waiting line (example values)
static Queue<int> num; //
static void Main(string[] args)
waitH = new AutoResetEvent(false); // initialize waiter
str = new Queue<string>();
num = new Queue<int>();
Thread thr = new Thread(new ThreadStart(Waiter)); // waiting in another thread
thr.Start(); // start the waiting thread
for(short i = 0; i < 10; i++)
str.Enqueue(string.Format($"(char)(i + 65)")); // add something to queue
num.Enqueue(i); // add a number to test incrementing
waitH.Set(); // signal to start the "long processing"
static void Waiter()
while(!whExit)
waitH.WaitOne(); // wait for signal
WriteToConsole(); // start the long processing on another thread
static void WriteToConsole()
// threadstart with parameters
// action: void delegate
// get 2 values from waiting line
var f = new ParameterizedThreadStart(obj =>
new Action<string, int>(ConsoleWriter)
(str.Dequeue(), num.Dequeue())); // it's thread safe, because FIFO?
Thread thr = new Thread(f);
thr.IsBackground = true; // close thread when finished
thr.Start();
// print to console
static void ConsoleWriter(string s, int n)
Console.WriteLine(string.Format($"s: ++n")); // easy example
它在 Main 的循环中停止。 我认为问题是:首先调用 Thread.Start() ,但它需要更改 Thread 的状态并加入“需要处理”队列,这需要时间。 Main 的循环已在运行,无需等待信号。
我用双向信号解决了这个问题:在循环中的 waitH.Set() 之后使用了另一个暂停信号 AutoResetEvent (WaitOne),并在完成 Console.WriteLine() 后发出信号。
我对这个解决方案并不真正感到自豪,因为如果我这样做,程序就会失去“线程”、并行或同步的方法。 这是一个例子,我想在不同的线程上同时运行长时间的计算。
如果我看到输出,那是我做错了一个适合书本的示例:
输出: 答:1 乙:2 有时 乙:2 答:1
预期输出: 答:1 乙:2 C: 3 D: 4 E:5 女:6 克:7 高:8 我:9 J:10
有什么优雅的方法可以解决这个问题吗?也许使用锁等。
任何讨论将不胜感激。 谢谢!
【问题讨论】:
【参考方案1】:有几个问题:
main 方法从不等待工作线程完成,因此它可能会运行到完成并在所有线程完成之前停止它们。这可以通过向工作线程发出停止信号来解决,然后使用thread.Join(
) 等待它完成。
WriteToConsole 从每个列表中获取一项并将其打印到控制台。但是线程可能会在 main 方法中的循环完成后启动。因此,当线程启动时,将发出 autoReset 事件并处理一项。但是在下一次迭代中, autoResetEvent 将被取消发出信号,并且永远不会再次发出信号。这可以通过在事件发出信号后遍历队列中的所有项目来解决。
像您提到的那样在循环中使用双向信号实际上会序列化代码,从而消除使用线程的任何好处。 如果这是一个学习练习,我建议先花时间学习Tasks、async/await、lock 和Parallel.For。如果你掌握了这些东西,你会比手动使用线程和重置事件更有效。// 它是线程安全的,因为 FIFO?
没有。如果您想要线程安全的集合,请使用 concurrent collections。
【讨论】:
感谢 JonasH 的回答。如果我使用 task = Task.Factory.StartNew(WriteToConsole) 而不是 waitH.Set(),并更改 WriteToConsole() : lock (locker) ConsoleWriter(str.Dequeue (), num.Dequeue());代码有效。我将继续执行任务。以上是关于C#多线程 - 出了啥问题,如何使用 AutoResetEvent的主要内容,如果未能解决你的问题,请参考以下文章
使用 discord.py 时在控制台中没有收到错误消息,我如何查看出了啥问题?
使用 Scipy 进行线性回归曲线拟合 - 不知道出了啥问题