How to properly synchronize this? At the moment it is possible that SetData
is called after e.WaitOne()
has completed so d
could be already set to another value. I tried to insert locks but it resulted into a deadlock.
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);
}
}
Will the best solution be to introduce a new boolean variable dataAlreadyBeenSetAndWaitingToBeProcessed
that is set in SetData
to true and at the end of DoLongOperationWith_d
it could be set to true, so if SetData
is called with this variable set to true it could just return?
This is untested, but is an elegant way to do this with the .net based primitives:
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);
}
}
}
Example program:
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);
}
}
This is a non-queue version, a queue version may be preferred:
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;
}
}
}