C#穿问题AutoResetEvent
-
18-09-2019 - |
题
如何正确进行同步这个吗?目前,它是可能的, SetData
是后调 e.WaitOne()
已完成了所以 d
可能是已经设置到另一个价值。我试着插入的锁,但它导致了进一僵局。
AutoResetEvent e = new AutoResetEvent(false);
public SetData(MyData d)
{
this.d=d;
e.Set(); // notify that new data is available
}
// This runs in separate thread and waits for d to be set to a new value
void Runner()
{
while (true)
{
e.WaitOne(); // waits for new data to process
DoLongOperationWith_d(d);
}
}
将最好的解决方案是要引入一个新布尔变量 dataAlreadyBeenSetAndWaitingToBeProcessed
这是设置在 SetData
要真正结束时 DoLongOperationWith_d
它可能被设置为真实,所以如果 SetData
是叫这个变量设定为真的这可能只是回来吗?
解决方案
这是未经测试,但是一种优雅的方式与基于.NET原语来做到这一点:
class Processor<T> {
Action<T> action;
Queue<T> queue = new Queue<T>();
public Processor(Action<T> action) {
this.action = action;
new Thread(new ThreadStart(ThreadProc)).Start();
}
public void Queue(T data) {
lock (queue) {
queue.Enqueue(data);
Monitor.Pulse(queue);
}
}
void ThreadProc() {
Monitor.Enter(queue);
Queue<T> copy;
while (true) {
if (queue.Count == 0) {
Monitor.Wait(queue);
}
copy = new Queue<T>(queue);
queue.Clear();
Monitor.Exit(queue);
foreach (var item in copy) {
action(item);
}
Monitor.Enter(queue);
}
}
}
实施例的程序:
class Program {
static void Main(string[] args) {
Processor<int> p = new Processor<int>((data) => { Console.WriteLine(data); });
p.Queue(1);
p.Queue(2);
Console.Read();
p.Queue(3);
}
}
这是一个非队列版本,队列版本可能是优选的:
object sync = new object();
AutoResetEvent e = new AutoResetEvent(false);
bool pending = false;
public SetData(MyData d)
{
lock(sync)
{
if (pending) throw(new CanNotSetDataException());
this.d=d;
pending = true;
}
e.Set(); // notify that new data is available
}
void Runner() // this runs in separate thread and waits for d to be set to a new value
{
while (true)
{
e.WaitOne(); // waits for new data to process
DoLongOperationWith_d(d);
lock(sync)
{
pending = false;
}
}
}
其他提示
有两个可能令人不安的情况在这里。
1:
- DoLongOperationWith_d(d)完成。
- 或者设置data()称,存储一个新的价值在d。
- e.使用此()称,但由于价值已经设置的线等待直到永远。
如果那是你的关心,我想你可以放松。从 文档, 我们看到,
如果有线电话使用此同时AutoResetEvent是在终止状态,线不块。该AutoResetEvent释放线,并立即返回到非发出信号的状态。
所以那不是个问题。然而,这取决于如何和何时或者设置data()是所谓的,你可以处理更严重的
2:
- 或者设置data()称,存储一个新的价值在d和唤醒亚军。
- DoLongOperationWith_d(d)开始。
- 或者设置data()再次调、储存新的价值在d。
- 或者设置data()称为了!老值d失去的永远;DoLongOperationWith_d()将永远不得援引。
如果这是你的问题,最简单的方法来解决这是一个并发的队列。实现中比比皆是。
可以使用2个事件,
AutoResetEvent e = new AutoResetEvent(false);
AutoResetEvent readyForMore = new AutoResetEvent(true); // Initially signaled
public SetData(MyData d)
{
// This will immediately determine if readyForMore is set or not.
if( readyForMore.WaitOne(0,true) ) {
this.d=d;
e.Set(); // notify that new data is available
}
// you could return a bool or something to indicate it bailed.
}
void Runner() // this runs in separate thread and waits for d to be set to a new value
{
while (true)
{
e.WaitOne(); // waits for new data to process
DoLongOperationWith_d(d);
readyForMore.Set();
}
}
一个你可以用这种方法做的事情是具有的SetData采取超时,并传递到WaitOne
。我想,无论你shoudl调查ThreadPool.QueueUserWorkItem
。
不隶属于 StackOverflow