I’m writing a C# wrapper, to a hardware controller using its Modbus protocol.
The controller has 12 inputs and 12 outputs.
The wrapper has two tasks:
1. Polling the controller’s inputs at a constant interval (i.e. 50ms).
2. Running preconfigured sequences, which changes the controller’s outputs.
The sequence is XML based:
<opcode>
<register>0</register>
<bit>1</bit>
<duration>500</duration>
</opcode>
<opcode>
<register>0</register>
<bit>0</bit>
<duration>0</duration>
</opcode>
....
In the above sample, the controller should turn output #0 On and after 500ms turns it Off.
The pauses between operations was achieved using Thread.Sleep().
In the past I’ve used just one BackgroundWorker. When not running the sequence it did the polling.
The challenge:
The new demand for the wrapper is that it could detect changes in the controller’s inputs while running a sequence.
I’ve modified the wrapper to have 2 backgroundworkers, one for polling and the other for setting the output registers.
Each of the BackgroundWorkers calls a seperate function on the controller, they do not try to access each other data nor they share any data.
Current code:
private void workerSequence_DoWork(object sender, DoWorkEventArgs e)
{
if (!terminating)
if (e.Argument != null)
DoSequence(e);
}
private void workerPoll_DoWork(object sender, DoWorkEventArgs e)
{
if (!terminating)
{
DoPoll();
Thread.Sleep(pollInterval);
}
}
private void DoSequence(DoWorkEventArgs e)
{
string sequenceName = e.Argument.ToString();
foreach (configurationSequencesSequenceOpcode opcode in sequencesList[sequenceName])
{
if (workerSequence.CancellationPending)
break;
byte register = opcode.register;
bool bit = opcode.bit;
int duration = opcode.duration;
SetRegister(register, bit, false);
Thread.Sleep(duration);
}
e.Result = e.Argument;
}
The problem:
It’s seems like the two BackgroundWorkers interfere with each other. I’ve tried using Semaphore, Monitor.Wait() and ManualResetEvent.WaitOne() but the BackgroundWorker, which deals with the sequences, doesn’t handles them well. The main issue – its sleep duration is not consistent as before.
Any advice will be welcome.
Using
Thread.Sleepoutside of test code is generally not ideal.You can use
System.Threading.Timerobjects for both these requirements.EDIT if you want to prevent concurrent calls, you can use a lock to implement mutual exclusion.
Create an object to lock on that can be seen by both timer handlers:
For the polling, you want to set up a timer like this:
I’ve set the period to
Timeout.Infinteso the timer doesn’t repeat. This meanspollIntervalis the time between one poll finishing and another starting – which will be different to the poll period ifDoPoll()takes some time.Doing it this way also prevents the timer ticking again while a previous poll is still running.
You can use the same principal to send the commands, but you will have to manage the current index into the
sequencesList:Or something along those lines ( but with suitable error handling! )
You can handle termination and cancellation by just disposing the timers.
BTW, there is an excellent ( and free ) ebook about threading that you could read:
Albahari: Threading in C#