Click here to Skip to main content
15,888,803 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi
Let's say you have a function like:

C#
public class MyClass
{
    //Asume that this for some reason needs to be a member variable
    private int count=0;  
    
    public async Task<int> GetCount()
    {
        ++count;
        
        //Could be some real work to do in a method that is declared async
        await Task.Delay(1); 

        return count;
    }
}


If you make an instance of MyClass called myInstanceMyClass and calls

C#
myInstanceMyClass.GetCount() 


on two different threads like thread1 and thread2 (using the same instance) then the result could be 1 and 2 or 2 and 2 right? Maybe just before thread1 return the count then thread2 could execute the ++count and then both threads could return 2 in myInstanceMyClass.GetCount().

I cannot make a lock because this gives a compile error:

C#
public async Task<int> GetCount()
{
    lock(myObject) //Gives compile error because of await inside the lock
    {
        ++count;
    
        //Could be some real work to do in a method that is declared async
        await Task.Delay(1);
    
        return count;
    }
}


So what can I do to make the async function GetCount() thread safe?

If the async GetCount() was writing the result to a file on the harddisk then maybe thread1 and thread2 could try to access the file at the same time. What can be done to solve this problem when the filewriting must be done inside an async function and lock() therefore cannot be used?

I can see on Google that someone uses some thirdparty projects to make some kind of

C#
using(var x= instance.Thirdparty()){... only 1 thread can go here at a time...}


but I cannot belive that we have to use thirdparty code to get the functionality that a lock() would give in a case with no await/async call. Are there any official recommendations from Microsoft? Or should we use some kind of programming pattern to "work around" the "await-lock"-problem?
Posted
Updated 21-Jan-16 3:03am
v3
Comments
F-ES Sitecore 21-Jan-16 9:30am    
Can't you make the calling code thread safe rather than the method itself? ie put locks around where it might call the method.
Afzaal Ahmad Zeeshan 21-Jan-16 9:35am    
Exactly!
Henrik R L 21-Jan-16 9:47am    
Hi
I think the calling code must make the call with "await Function" and then you will again have a compile error when making a lock in the calling code.

Assuming you want the return values to be 1 and 2, use the Interlocked.Increment Method[^] to increment the field, and store the result in a local variable to return at the end of the method:
C#
public class MyClass
{
    private int count = 0;
    
    public async Task<int> GetCount()
    {
        int result = Interlocked.Increment(ref count);
        
        await Task.Delay(1); 
 
        return result;
    }
}

If you want to limit the number of async tasks which can access a protected resource at the same time, then you need something like Stephen Toub's AsyncSemaphore[^] class:
C#
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public sealed class AsyncSemaphore
{
    private readonly Queue<TaskCompletionSource<IDisposable>> _waiters = new Queue<TaskCompletionSource<IDisposable>>();
    private readonly WaitCallback _releaseCoreCallback;
    private readonly Task<IDisposable> _releaserTask;
    private readonly Releaser _releaser;
    private readonly int _maxCount;
    private int _currentCount;

    public AsyncSemaphore(int initialCount)
    {
        if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");

        _maxCount = _currentCount = initialCount;

        _releaser = new Releaser(this);
        _releaserTask = Task.FromResult((IDisposable)_releaser);
        _releaseCoreCallback = ReleaseCore;
    }

    public Task<IDisposable> WaitAsync()
    {
        lock (_waiters)
        {
            if (_currentCount > 0)
            {
                _currentCount--;
                return _releaserTask;
            }

            var waiter = new TaskCompletionSource<IDisposable>();
            _waiters.Enqueue(waiter);
            return waiter.Task;
        }
    }

    private void Release()
    {
        TaskCompletionSource<IDisposable> toRelease = null;
        lock (_waiters)
        {
            if (_waiters.Count > 0)
            {
                toRelease = _waiters.Dequeue();
            }
            else if (_currentCount < _maxCount)
            {
                _currentCount++;
            }
            else
            {
                throw new SemaphoreFullException();
            }
        }
        if (null != toRelease)
        {
            ThreadPool.QueueUserWorkItem(_releaseCoreCallback, toRelease);
        }
    }

    private void ReleaseCore([NotNull] object state)
    {
        ((TaskCompletionSource<IDisposable>)state).SetResult(_releaser);
    }

    private sealed class Releaser : IDisposable
    {
        private readonly AsyncSemaphore _throttle;

        public Releaser(AsyncSemaphore throttle)
        {
            _throttle = throttle;
        }

        public void Dispose()
        {
            _throttle.Release();
        }
    }
}

Usage would look something like this:
C#
public class MyClass
{
    // Only one task can access the file at a time:
    private static readonly AsyncSemaphore FileSemaphore = new AsyncSemaphore(1);
    
    public async Task DoSomething()
    {
        ...
        
        using (await FileSemaphore.WaitAsync())
        {
            // Access the file within this block...
        }
        
        ...
    }
}

Stephen wrote a whole series of blog posts on these async primitives:

 
Share this answer
 
v2
Comments
Henrik R L 21-Jan-16 12:11pm    
Hi Richard Deeming

Thanks for the answer. I think your last solution with using (await FileSemaphore.WaitAsync()){...} is the one I will use. The reason for my question is that I'm working on a WCF Service that must run in Azure. This WCF Service will have some functions that can be called from many applications around the world at the same time and each application can also call these WCF Service functions on different threads at the same time. In the beginning the WCF Service functions will handle jobs like searching for documents in a SQL Server (also in Azure). The WCF Service functions will contain async LINQ functions (from Entity Framework 6) and I was thinking what will happen if thread1 and thread2 both was modifying some temporary variables that will be used in other async LINQ expressions. So there will several async LINQ statements in one function like:

var x = await "some query".ToListAsync();
var y = await "some query with x".ToListAsync();
var result = await "some query with y".ToListAsync();

So I think that your last solution is best to make sure that thread2 will not change lets say y just after thread1 has calculated y and is about to calculate result based on y?
Richard Deeming 21-Jan-16 12:15pm    
Since those are all local variables, they can't be modified from other threads.

It sounds more like you want to prevent other threads from modifying the data in your SQL database whilst your task is running. If that's the case, you'll want to use a database transaction with the appropriate isolation level:
Working with Transactions (EF6 Onwards)[^]
Henrik R L 21-Jan-16 12:30pm    
Hi Richard Deeming

Yes thank you, I will base the functions on local variables and database transactions. This way I can solve the problems.
Because as per C#, it is illegal to do so.

An await expression cannot be used in a synchronous function, in a query expression, in the catch or finally block of an exception handling statement, in the block of a lock statement, or in an unsafe context.

The thing is, if you call this code again and again, the await keyword would take the code back to from where it was called, and then you would come back to the same code execution, this threading of lock, unlock would cause a trouble in your code, maybe a deadlock, and basically you won't be able to get what you wanted to get.

Secondly, what Jon suggested on that answer is something like this:
C#
// Now it's clear where the locks will be acquired and released
lock (foo)
{
}
var result = await something;
lock (foo)
{
}

You would then execute the code that would alter the value of "foo", and then you will continue again.

Read Eric Lippert and Jon Skeet's answers to this thread, c# - Why can&#39;t I use the &#39;await&#39; operator within the body of a lock statement? - Stack Overflow[^]
 
Share this answer
 
Comments
Henrik R L 21-Jan-16 9:45am    
Hi Afzall

In your suggestion with a lock, await and then lock I think that thread1 could be just about to execute "await something" and then thread2 are allowed to go into the first lock and change a variable myVariable that we could asume would be used in the call "await something(myVariable)". Then thread1 will make the call "await something(myVariable)" with myVariable having a different value (changed by thread2). So I think there still is a problem?
Afzaal Ahmad Zeeshan 21-Jan-16 9:48am    
Not mine, that was what was included in the answer that I referenced. The thing is that C# does not allow using await in lock environment.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900