I am writing a class that contains data. It exposes methods that allow to query the data, while the data is also being updated from an external source (web service, for example).
All the methods expose tasks that are started on a worker thread using the default task scheduler. So the structure of the class is as follows:
public class A
{
private object _myLock;
public Task<int> GetSomeNumber(int aParameter)
{
return Task.Run(() =>
{
lock (_myLock)
{
int result = 0;
// calculate the result...
// ...
return result;
}
});
}
public Task<string> GetSomeText(int aParameter)
{
return Task.Run(() =>
{
lock (_myLock)
{
string result = "";
// calculate the result...
// ...
return result;
}
});
}
}
The methods may be called several multiple times, while the data itself is being updated, so the calculations that require the shared data are surrounded with a lock statement. The problem is that this code blocks the thread in which the task is run. Of course, these are not UI thread, so the UI remains responsive, but it does consume threads and keeps them blocked. I would prefer for the worker threads in the thread pool to remain as available as possible.
Is there a way, a best practice of some sort, that allows to write this code so that the shared data is thread safe and at the same time the tasks are not blocked? If instead of waiting for a lock, the tasks would need to wait for something else to finish (a task), I would use ContinueWith
in order to create a continuation task and return it, so that it would be scheduled to run when the first task is completed. Is there a way to do the same with lock, so that the task is only scheduled when the lock is available?
Update
I have done some reading and found out about several possible options that allow for shared data sync. These include: SemaphoreSlim
, AsyncLock
, and ConcurrentExclusiveSchedulerPair
.
I would love to see a recommendation for a best practice, of writing a service class that provides "read only" task methods that can run concurrently with a read lock, and "write" task methods that require write lock.