C# 多线程 - 怎么了,如何使用 AutoResetEvent
C# Multi-Threading - What's wrong, how to use AutoResetEvent
我还在学习线程,我在使用下面这段代码时遇到了问题。抱歉,如果这个问题之前出现过,我真的不明白为什么这段代码不起作用。
我简化了代码:
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 的状态并加入 "need to be processed" queue ,这需要时间。 Main 的循环已经 运行ning 而不是等待信号。
我用双向信号解决了这个问题:在循环中 waitH.Set() 之后使用另一个暂停信号 AutoResetEvent (WaitOne) 并在完成 Console.WriteLine() 后发出信号。
我并不真正为这个解决方案感到自豪,因为如果我这样做,程序就会失去 "threadish"、并行或同步方法。
这是一个例子,我想 运行 同时在不同的线程上进行长时间的计算。
如果我看到了输出,那是我做错的书装示例:
输出:
答:1
乙:2
有时
乙:2
答:1
预期输出:
答:1
乙:2
中:3
D:4
电子:5
女:6
体重:7
身高:8
我:9
J: 10
有什么优雅的方法可以解决这个问题吗?可能要用锁等
如有任何讨论,我们将不胜感激。
谢谢!
有几个问题:
main 方法从不等待工作线程完成,因此它可能会 运行 完成并在完成之前停止所有线程。这可以通过通知工作线程停止,然后使用 thread.Join(
) 等待它完成来解决。
WriteToConsole 从每个列表中取出一项并将其打印到控制台。但是线程可能会在 main 方法中的循环完成后启动。因此,当线程启动时,将发出 autoReset 事件信号并处理一项。但在下一次迭代中,autoResetEvent 将不再发出信号,并且永远不会再次发出信号。这可以通过在事件发出信号后遍历队列中的所有项目来解决。
- 像您提到的那样在循环中使用双向信号实际上会序列化代码,从而消除使用线程的任何好处。
- 如果这是一个学习练习,我建议先花时间学习 Tasks, async/await, lock and Parallel.For。如果你能很好地掌握这些东西,你将比手动使用线程和重置事件更有效。
// it's thread safe, because FIFO?
没有。如果需要线程安全集合,请使用 concurrent collections。
我还在学习线程,我在使用下面这段代码时遇到了问题。抱歉,如果这个问题之前出现过,我真的不明白为什么这段代码不起作用。 我简化了代码:
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 的状态并加入 "need to be processed" queue ,这需要时间。 Main 的循环已经 运行ning 而不是等待信号。
我用双向信号解决了这个问题:在循环中 waitH.Set() 之后使用另一个暂停信号 AutoResetEvent (WaitOne) 并在完成 Console.WriteLine() 后发出信号。
我并不真正为这个解决方案感到自豪,因为如果我这样做,程序就会失去 "threadish"、并行或同步方法。 这是一个例子,我想 运行 同时在不同的线程上进行长时间的计算。
如果我看到了输出,那是我做错的书装示例:
输出: 答:1 乙:2 有时 乙:2 答:1
预期输出: 答:1 乙:2 中:3 D:4 电子:5 女:6 体重:7 身高:8 我:9 J: 10
有什么优雅的方法可以解决这个问题吗?可能要用锁等
如有任何讨论,我们将不胜感激。 谢谢!
有几个问题:
main 方法从不等待工作线程完成,因此它可能会 运行 完成并在完成之前停止所有线程。这可以通过通知工作线程停止,然后使用
thread.Join(
) 等待它完成来解决。WriteToConsole 从每个列表中取出一项并将其打印到控制台。但是线程可能会在 main 方法中的循环完成后启动。因此,当线程启动时,将发出 autoReset 事件信号并处理一项。但在下一次迭代中,autoResetEvent 将不再发出信号,并且永远不会再次发出信号。这可以通过在事件发出信号后遍历队列中的所有项目来解决。
- 像您提到的那样在循环中使用双向信号实际上会序列化代码,从而消除使用线程的任何好处。
- 如果这是一个学习练习,我建议先花时间学习 Tasks, async/await, lock and Parallel.For。如果你能很好地掌握这些东西,你将比手动使用线程和重置事件更有效。
// it's thread safe, because FIFO?
没有。如果需要线程安全集合,请使用 concurrent collections。