I have a following method
public async Task<T> SomeMethod(parameters)
{
// here we execute some instructions which are not thread safe
}
I need SomeMethod to return a Task, so that other methods can run (await) it asynchronously, and not block the UI thread.
The problem is that SomeMethod can be called in parallel, since the execution is returned to UI thread, and that will raise exception, since some of the calls inside SomeMethod() are not thread safe.
What is the best way to ensure that all calls to SomeMethod are queued (and awaitable), and that this queue will be executed in sequence?
Use AsyncLock to prevent two threads from executing a single block of code :
(A traditional lock will not work, because you can't use an await keyword inside of it)
private AsyncLock myAsyncLock = new AsyncLock();
public async Task<T> SomeMethod(parameters)
{
using (await myAsyncLock.LockAsync())
{
// here we execute some instructions which are not thread safe
}
}
public class AsyncLock
{
private readonly AsyncSemaphore m_semaphore;
private readonly Task<Releaser> m_releaser;
public AsyncLock()
{
m_semaphore = new AsyncSemaphore(1);
m_releaser = Task.FromResult(new Releaser(this));
}
public Task<Releaser> LockAsync()
{
var wait = m_semaphore.WaitAsync();
return wait.IsCompleted ?
m_releaser :
wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
this, System.Threading.CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public struct Releaser : IDisposable
{
private readonly AsyncLock m_toRelease;
internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; }
public void Dispose()
{
if (m_toRelease != null)
m_toRelease.m_semaphore.Release();
}
}
}
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx
public class AsyncSemaphore
{
private readonly static Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
private int m_currentCount;
public AsyncSemaphore(int initialCount)
{
if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
m_currentCount = initialCount;
}
public Task WaitAsync()
{
lock (m_waiters)
{
if (m_currentCount > 0)
{
--m_currentCount;
return s_completed;
}
else
{
var waiter = new TaskCompletionSource<bool>();
m_waiters.Enqueue(waiter);
return waiter.Task;
}
}
}
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}
}
Related
Lets say I have a worker class.
public sealed class Worker : IDisposable
{
private bool _isRunning;
private CancellationTokenSource _cts;
private readonly Action _action;
private readonly int _millisecondsDelay;
private readonly object _lock = new object();
public Worker(Action action, int millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay = 5000;
}
public void Start()
{
lock (_lock)
{
if (!_isRunning)
{
_isRunning = true;
Run();
}
}
}
public void Cancel()
{
lock (_lock)
{
if (_isRunning) _cts.Cancel();
}
}
private void Run()
{
using (_cts) _cts = new CancellationTokenSource();
Task.Run(async () => { await DoAsync(_cts.Token); });
}
private async Task DoAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
//this code is unreachable
lock (_lock)
{
_isRunning = false;
}
}
public void Dispose()
{
try
{
_cts?.Cancel();
}
finally
{
if (_cts != null)
{
_cts.Dispose();
_cts = null;
}
}
}
}
The problem is the code _isRunning = false; is unreachable. I mean more likely when a caller call Cancel method the worker will be awaiting Task.Delay. So how I can call smth(here it's _isRunning = false;) after my Task will be canceled ? In other words I need to be sure that my worker is not running(it's not the cancelled state)
To answer your literal question, you can use a finally block:
private async Task DoAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
But I have some concerns about this "worker" approach:
I'm not a huge fan of the fire-and-forget inside Run. I suspect you'll want to change that.
Mixing lock with asynchronous code can be problematic. You should be absolutely sure that this is what you really want to do.
It may be worthwhile stepping back and reconsidering what you are actually wanting to do with this code.
From the documentation here: "The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads..."
Also, an answer to this question states "INTERLOCKED METHODS ARE CONCURRENTLY SAFE ON ANY NUMBER OF COREs OR CPUs" which seems pretty clear.
Based on the above I thought Interlocked.Add() would be sufficient for multiple threads to do addition on a variable. Apparently I'm wrong or I'm using the method incorrectly. In the runnable code below I expect Downloader.ActiveRequestCount to be zero when Run() completes. If I do not lock around the call to Interlocked.Add I get a random non-zero result. What is the correct usage of Interlocked.Add()?
class Program
{
private Downloader downloader { get; set; }
static void Main(string[] args)
{
new Program().Run().Wait();
}
public async Task Run()
{
downloader = new Downloader();
List<Task> tasks = new List<Task>(100);
for (int i = 0; i < 100; i++)
tasks.Add(Task.Run(Download));
await Task.WhenAll(tasks);
Console.Clear();
//expected:0, actual when lock is not used:random number i.e. 51,115
Console.WriteLine($"ActiveRequestCount is : {downloader.ActiveRequestCount}");
Console.ReadLine();
}
private async Task Download()
{
for (int i = 0; i < 100; i++)
await downloader.Download();
}
}
public class Downloader :INotifyPropertyChanged
{
private object locker = new object();
private int _ActiveRequestCount;
public int ActiveRequestCount { get => _ActiveRequestCount; private set => _ActiveRequestCount = value; }
public async Task<string> Download()
{
string result = string.Empty;
try
{
IncrementActiveRequestCount(1);
result = await Task.FromResult("boo");
}
catch (Exception ex)
{
Console.WriteLine("oops");
}
finally
{
IncrementActiveRequestCount(-1);
}
return result;
}
public void IncrementActiveRequestCount(int value)
{
//lock (locker) // is this redundant
//{
_ActiveRequestCount = Interlocked.Add(ref _ActiveRequestCount, value);
//}
RaisePropertyChanged(nameof(ActiveRequestCount));
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberNameAttribute] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
Replace
_ActiveRequestCount = Interlocked.Add(ref _ActiveRequestCount, value);
with
Interlocked.Add(ref _ActiveRequestCount, value);
Interlocked.Add is thread-safe and takes a ref parameter, so that it can do the assignment safely. You additionally perform an (unnecessary) unsafe assignment (=). Just remove it.
What is the proper way to ensure that only the 'last-in' thread is given access to a mutex/locked region while intermediary threads do not acquire the lock?
Example sequence:
A acquires lock
B waits
C waits
B fails to acquire lock*
A releases lock
C acquires lock
*B should fail to acquire the lock either via an exception (as in SemaphoreSlim.Wait(CancellationToken) or a boolean Monitor.TryEnter() type construct.
I can think of several similar schemes to achieve this (such as using a CancellationTokenSource and SemaphoreSlim), but none of them seem particularly elegant.
Is there a common practice for this scenario?
This should work like you want, it uses a SemaphoreSlim with a size of 1 to control it. I also added support for passing in a CancelationToken to cancel waiting for the lock early, it also supports WaitAsync returning a task instead of blocking.
public sealed class LastInLocker : IDisposable
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private CancellationTokenSource _cts = new CancellationTokenSource();
private bool _disposed = false;
public void Wait()
{
Wait(CancellationToken.None);
}
public void Wait(CancellationToken earlyCancellationToken)
{
if(_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
_semaphore.Wait(token);
}
public Task WaitAsync()
{
return WaitAsync(CancellationToken.None);
}
public async Task WaitAsync(CancellationToken earlyCancellationToken)
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
var token = ReplaceTokenSource(earlyCancellationToken);
//I await here because if ReplaceTokenSource thows a exception I want the
//observing of that exception to be deferred until the caller awaits my
//returned task.
await _semaphore.WaitAsync(token).ConfigureAwait(false);
}
public void Release()
{
if (_disposed)
throw new ObjectDisposedException("LastInLocker");
_semaphore.Release();
}
private CancellationToken ReplaceTokenSource(CancellationToken earlyCancellationToken)
{
var newSource = CancellationTokenSource.CreateLinkedTokenSource(earlyCancellationToken);
var oldSource = Interlocked.Exchange(ref _cts, newSource);
oldSource.Cancel();
oldSource.Dispose();
return newSource.Token;
}
public void Dispose()
{
_disposed = true;
_semaphore.Dispose();
_cts.Dispose();
}
}
Here is a little test program that re-creates your test example
internal class Program
{
static LastInLocker locker = new LastInLocker();
private static void Main(string[] args)
{
Task.Run(() => Test("A"));
Thread.Sleep(500);
Task.Run(() => Test("B"));
Thread.Sleep(500);
Task.Run(() => Test("C"));
Console.ReadLine();
}
private static void Test(string name)
{
Console.WriteLine("{0} waits for lock", name);
try
{
locker.Wait();
Console.WriteLine("{0} acquires lock", name);
Thread.Sleep(4000);
locker.Release();
Console.WriteLine("{0} releases lock", name);
}
catch (Exception)
{
Console.WriteLine("{0} fails to acquire lock", name);
}
}
}
outputs
A waits for lock
A acquires lock
B waits for lock
C waits for lock
B fails to acquire lock
A releases lock
C acquires lock
C releases lock
Try this:
public interface ILocker
{
bool GetLock();
void Release();
}
class Locker : ILocker
{
private long m_NumberOfTimeGetLockWasCalled = 0;
private readonly object m_LockingObject = new object();
private readonly object m_LockingObject2 = new object();
public bool GetLock()
{
long lock_count = 0;
var lock_was_taken = false;
lock(m_LockingObject)
{
lock_count = m_NumberOfTimeGetLockWasCalled++;
lock_was_taken = Monitor.TryEnter(m_LockingObject2);
if (lock_was_taken)
return true;
}
while(!lock_was_taken)
{
Thread.Sleep(5);
lock(m_LockingObject)
{
if (lock_count != m_NumberOfTimeGetLockWasCalled)
return false;
lock_was_taken = Monitor.TryEnter(m_LockingObject2);
if (lock_was_taken)
break;
}
}
return true;
}
public void Release()
{
Monitor.Exit(m_LockingObject2);
}
}
I need to code my own FIFO/strong semaphore in C#, using a semaphore class of my own as a base. I found this example, but it's not quite right since I'm not supposed to be using Monitor.Enter/Exit yet.
These are the methods for my regular semaphore, and I was wondering if there was a simple way to adapt it to be FIFO.
public virtual void Acquire()
{
lock (this)
{
while (uintTokens == 0)
{
Monitor.Wait(this);
}
uintTokens--;
}
}
public virtual void Release(uint tokens = 1)
{
lock (this)
{
uintTokens += tokens;
Monitor.PulseAll(this);
}
}
So SemaphoreSlim gives us a good starting place, so we'll begin by wrapping one of those in a new class, and directing everything but the wait method to that semaphore.
To get a queue like behavior we'll want a queue object, and to make sure it's safe in the face of multithreaded access, we'll use a ConcurrentQueue.
In this queue we'll put TaskCompletionSource objects. When we want to have something start waiting it can create a TCS, add it to the queue, and then inform the semaphore to asynchronously pop the next item off of the queue and mark it as "completed" when the wait finishes. We'll know that there will always be an equal or lesser number of continuations as there are items in the queue.
Then we just wait on the Task from the TCS.
We can also trivially create a WaitAsync method that returns a task, by just returning it instead of waiting on it.
public class SemaphoreQueue
{
private SemaphoreSlim semaphore;
private ConcurrentQueue<TaskCompletionSource<bool>> queue =
new ConcurrentQueue<TaskCompletionSource<bool>>();
public SemaphoreQueue(int initialCount)
{
semaphore = new SemaphoreSlim(initialCount);
}
public SemaphoreQueue(int initialCount, int maxCount)
{
semaphore = new SemaphoreSlim(initialCount, maxCount);
}
public void Wait()
{
WaitAsync().Wait();
}
public Task WaitAsync()
{
var tcs = new TaskCompletionSource<bool>();
queue.Enqueue(tcs);
semaphore.WaitAsync().ContinueWith(t =>
{
TaskCompletionSource<bool> popped;
if (queue.TryDequeue(out popped))
popped.SetResult(true);
});
return tcs.Task;
}
public void Release()
{
semaphore.Release();
}
}
I have created a FifoSemaphore class and I am successfully using it in my solutions. Current limitation is that it behaves like a Semaphore(1, 1).
public class FifoSemaphore
{
private readonly object lockObj = new object();
private List<Semaphore> WaitingQueue = new List<Semaphore>();
private Semaphore RequestNewSemaphore()
{
lock (lockObj)
{
Semaphore newSemaphore = new Semaphore(1, 1);
newSemaphore.WaitOne();
return newSemaphore;
}
}
#region Public Functions
public void Release()
{
lock (lockObj)
{
WaitingQueue.RemoveAt(0);
if (WaitingQueue.Count > 0)
{
WaitingQueue[0].Release();
}
}
}
public void WaitOne()
{
Semaphore semaphore = RequestNewSemaphore();
lock (lockObj)
{
WaitingQueue.Add(semaphore);
semaphore.Release();
if(WaitingQueue.Count > 1)
{
semaphore.WaitOne();
}
}
semaphore.WaitOne();
}
#endregion
}
Usage is just like with a regular semaphore:
FifoSemaphore fifoSemaphore = new FifoSemaphore();
On each thread:
fifoSemaphore.WaitOne();
//do work
fifoSemaphore.Release();
I was experimenting with the new C# await feature. I made a custom awaiter implementation as follows:
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
T1();
Console.WriteLine("After t1");
}
private static async void T1()
{
CustomAwaitable a = new Sleeper().Sleep();
object r = await a;
Console.WriteLine("sleeper awakes " + r);
}
}
internal class CustomAwaitable
{
private readonly Sleeper m_sleeper;
public CustomAwaitable(Sleeper s)
{
m_sleeper = s;
}
public MyAwaiter GetAwaiter()
{
return new MyAwaiter(m_sleeper);
}
}
internal class Sleeper
{
public ManualResetEvent Handle = new ManualResetEvent(false);
public bool Awake { get; set; }
public int Result
{
get { return Environment.TickCount; }
}
public CustomAwaitable Sleep()
{
new Thread(() =>
{
Thread.Sleep(5000);
Awake = true;
Handle.Set();
}).Start();
Console.WriteLine("begin sleeping " + Result);
return new CustomAwaitable(this);
}
}
internal class MyAwaiter : INotifyCompletion
{
private readonly Sleeper m_sleeper;
public MyAwaiter(Sleeper sleeper)
{
m_sleeper = sleeper;
}
public bool IsCompleted
{
get { return m_sleeper.Awake; }
}
public void OnCompleted(Action continuation)
{
// This works!!
//new Thread(() =>
//{
// m_sleeper.Handle.WaitOne();
// continuation();
//}).Start();
// This doesn't work!!
Action k = () =>
{
m_sleeper.Handle.WaitOne();
continuation();
};
k.BeginInvoke(null, null);
}
public object GetResult()
{
return m_sleeper.Result;
}
}
}
The problem is that, in the OnCompleted method, when I schedule the continuation code execution using BeginInvoke, the GetResult method is never called. But when I create a thread manually to do the same thing, everything works as expected. I know that BeginInvoke uses the thread pool internally, which should work the same way as the thread approach (I know that there is a thread count limit with thread pool, but it is negligible in this case as I am not running anything else).
What are your ideas? Thanks!