What's the best way to prevent the async method from running even before it finishes?

I have this pattern to prevent the async method from being called before it had time to finish earlier.

My solution of having to use a flag and then having to lock around the flag seems to be quite verbose. Is there a more natural way to achieve this?

public class MyClass
{
    private object SyncIsFooRunning = new object();
    private bool IsFooRunning { get; set;}

    public async Task FooAsync()
    {
        try
        {
            lock(SyncIsFooRunning)
            {
                if(IsFooRunning)
                    return;

                IsFooRunning = true;
            }

            // Use a semaphore to enforce maximum number of Tasks which are able to run concurrently.
            var semaphoreSlim = new SemaphoreSlim(5);
            var trackedTasks = new List<Task>();

            for(int i = 0; i < 100; i++)
            {
                await semaphoreSlim.WaitAsync();

                trackedTasks.Add(Task.Run(() =>
                {
                    // DoTask();
                    semaphoreSlim.Release();
                }));
            }

            // Using await makes try/catch/finally possible.
            await Task.WhenAll(trackedTasks);
        }
        finally
        {
            lock(SyncIsFooRunning)
            {
                IsFooRunning = false;
            }
        }
    }
}

      

+3


source to share


1 answer


As noted in the comments, you can use Interlocked.CompareExchange()

if you like:

public class MyClass
{
    private int _flag;

    public async Task FooAsync()
    {
        try
        {
            if (Interlocked.CompareExchange(ref _flag, 1, 0) == 1)
            {
                return;
            }

            // do stuff
        }
        finally
        {
            Interlocked.Exchange(ref _flag, 0);
        }
    }
}

      

However, I think this is overkill. Nothing wrong with lock

this type of scenario, especially if you don't expect a lot of controversy over this method. What I think would be better is to wrap the method so that the caller can always await

as a result, whether a new asynchronous operation has been started or not:

public class MyClass
{
    private readonly object _lock = new object();
    private Task _task;

    public Task FooAsync()
    {
            lock (_lock)
            {
                return _task != null ? _task : (_task = FooAsyncImpl());
            }
    }

    public async Task FooAsyncImpl()
    {
        try
        {
            // do async stuff
        }
        finally
        {
            lock (_lock) _task = null;
        }
    }
}

      



Finally, in the comments, you say the following:

It seems a little odd that all return types are still valid for Task?

It is not clear what you mean by this. In your method, the only valid return types are void

and Task

. If your operator return

returned an actual value, you will need to use Task<T>

, where T

is the type returned by the return operator.

+2


source







All Articles