如何正确同步?目前,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
引入true,并在DoLongOperationWith_d
结束时将其设置为true,所以如果{调用{1}}时将此变量设置为true,它可以返回吗?
答案 0 :(得分:5)
这是未经测试的,但使用基于.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 :(得分:2)
这里有两个可能令人不安的情况。
如果那是你的担忧,我想你可以放松一下。从documentation开始,我们看到了
如果线程在AutoResetEvent处于信号状态时调用WaitOne,则线程不会阻塞。 AutoResetEvent立即释放线程并返回到非信号状态。
所以这不是问题。但是,根据调用SetData()的方式和时间,您可能正在处理更严重的
如果这是你的问题,解决它的最简单方法是并发队列。实施比比皆是。
答案 2 :(得分:1)
您可以使用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
。不过我想你会调查ThreadPool.QueueUserWorkItem
。