I want to have a thread that have a permanent lifetime (think of it like a process) and that manages a resource (a critical section in my case it's a 3G modem accessed via a serial port).
Other threads will send requests to this thread via a method and wait until it sends a response or a timeout has expired.
I can achieve this with the Qt C framework but I didn't find an easy way to do it in C# (I already asked a question the other time and someone told me about the threading context but he didn't answer my question), I have to write something like Qt's signal/slot system and it's a tedious task.
A colleague told me to take a look at AsyncEx but I haven't found an example that illustrates what I want to achieve and I'm not sure if it will help (neither will the async/await stuff).
It really pisses me off because I have a C# buggy code that uses locks (concurrency bugs and no it's not me who wrote it) and I would like to use this threading model to refactor it.
It's unbelievable that only Qt has done something powerful to handle this case (thread aware signals/slots and event loops), the others only offer useless garbage.
Update : In Qt, we can't invoke a method with timeout. It will block until the other thread sends a reply and according to this https://bugreports.qt.io/browse/QTBUG-63870 it's better to perform a non-blocking request...
Update 2 : Following JonasH's answer, I made this example : https://github.com/embeddedmz/message_passing_on_csharp which could be useful for people who share the same point of view as me concerning the threading model (message passing) which in my opinion is much less harmful than others (you know the multi-threaded programs which work well most of the time, for me it's binary it's either works all the time or nothing).
I will share another solution found by my work colleagues when they will show it to me in a future update.
CodePudding user response:
One way would be with a shared LimitedConcurrencyLevelTaskScheduler (see example). That could ensure only a single thread has exclusive access to the resource at any one time, while not needing the block a thread when the resource in not needed.
The typical way to write this functionality yourself would be with a blocking collection. I.e. the worker thread would run a foreach loop over the GetConsumingEnumerable and process each item. A very simplified example could look something like:
private TResource resource;
private BlockingCollection<Action<TResource>> queue = new();
public void Start() => Task.Run(ThreadMain);
public Task<T> Enqueue<T>(Func<TResource, T> method)
{
var tcs = new TaskCompletionSource<T>();
queue.Add(r => tcs.SetResult(method(r)));
return tcs.Task;
}
private void ThreadMain()
{
foreach (var action in queue.GetConsumingEnumerable())
{
action(resource);
}
}
But you would need to add things like error handling, cancellation, stopping the thread etc.
Another alternative might be a semaphoreSlim with a limit of one, and use WaitAsync with async/await to ensure exclusive access. I don't think a semaphore would guarantee any particular ordering if that is important to you. You may also risk blocking the UI thread if the work takes time.
There are also frameworks like DataFlow that might help, but I'm not familiar enough with it to provide any recommendation.
For a introduction to task based programming, see Task-based asynchronous programming and Asynchronous programming with async and await.