How to achieve sequential blocking behavior in multithread application?

517 views Asked by At

I'm writing an application that should simulate the behavior of a PLC. This means I have to run several threads making sure only one thread at a time is active and all others are suspended. For example:

  • thread 1 repeats every 130ms and blocks all other threads. The effective runtime is 30ms and the remaining 100ms before the thread restarts can be used by other threads.
  • thread 2 repeats every 300ms and blocks all threads except for thread 1. The effective runtime is 50ms (the remaining 250ms can be used by other threads). Thread 2 is paused until thread 1 has finished executing code (the remaining 100ms of thread 1) and once thread 1 is asleep it resumes from where it has been paused
  • thread 3 repeats every 1000ms. The effective runtime is 100ms. This thread continues execution only if all other threads are suspended.

The highest priority is to complete the tasks before they are called again, otherwise I have to react, therefore a thread that should be blocked should not run until a certain point, otherwise multicore processing would elaborate the code and only wait to pass the results.

I read several posts and learned that Thread.suspend is not recomended and semaphore or monitor operations mean that the code is executed until a specific and fixed point in the code while I have to pause the threads exactly where the execution has arrived when an other thread (with higher "priority") is called.

I also looked at the priority setting but it doesn't seem to be 100% relevant since the system can override priorities.

Is there a correct or at least solid way to code the blocking mechanism?

2

There are 2 answers

3
Luaan On BEST ANSWER

I don't think you need to burden yourself with Threads at all. Instead, you can use Tasks with a prioritised TaskScheduler (it's not too hard to write or find by googling).

This makes the code quite easy to write, for example the highest priority thread might be something like:

while (!cancellationRequested)
{
  var repeatTask = Task.Delay(130);

  // Do your high priority work      

  await repeatTask;
}

Your other tasks will have a similar basic layout, but they will be given a lower priority in the task scheduler (this is usually handled by the task scheduler having a separate queue for each of the task priorities). Once in a while, they can check whether there is a higher priority task, and if so, they can do await Task.Yield();. In fact, in your case, it seems like you don't even need real queues - that makes this a lot easier, and even better, allows you to use Task.Yield really efficiently.

The end result is that all three of your periodic tasks are efficiently run on just a single thread (or even no thread at all if they're all waiting).

This does rely on coöperative multi-tasking, of course. It's not really possible to handle full blown real-time like pre-emption on Windows - and partial pre-emption solutions tend to be full of problems. If you're in control of most of the time spent in the task (and offload any other work using asynchronous I/O), the coöperative solution is actually far more efficient, and can give you a lot less latency (though it really shouldn't matter much).

4
Leonard Klausmann On

I hope I don't missunderstand your question :)

One possibility to your problem might be to use a concurrent queue: https://msdn.microsoft.com/de-de/library/dd267265(v=vs.110).aspx

For example you create a enum to control your state and init the queue:

 private ConcurrentQueue<Action> _clientActions ;
     private enum Statuskatalog
      {
           Idle,
           Busy
       };

Create a timer to start and create a timerfunktion.

Timer _taskTimer = new Timer(ProcessPendingTasks, null, 100, 333);



private void ProcessPendingTasks(object x)
         {
           _status = Statuskatalog.Busy;
             _taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
             Action currentTask;
             while( _clientActions.TryDequeue( out currentTask ))
             {
                 var task = new Task(currentTask);
                 task.Start();         
                 task.Wait();
             }

         _status=Statuskatalog.Idle;
         }

Now you only have to add your tasks as delegates to the queue:

_clientActions.Enqueue(delegate { **Your task** });
            if (_status == Statuskatalog.Idle) _taskTimer.Change(0, 333);

On this base, you can manage your special requirements you were asking for.

Hope this was, what you were searching for.