Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to protect resources that may be used in a multi-threaded or async environment?

I am working on a C# API that is used by a variety of consumers. This API provides access to a shared resource (in my case hardware that does serial communication) , that will often have a few different actors trying to use it concurrently.

The issue I have is that some of my consumers will want to use this in a multi-threaded environment - each actor works independently and try to use the resource. A simple lock works fine here. But some of my consumers would prefer to use async-await and time-slice the resource. (As I understand it) this requires an asynchronous lock to yield the timeslice back to other tasks; blocking at a lock would halt that whole thread.

And I imagine that having serial locks is unperformant at best, and a potential race condition or deadlock at worst.

So how can I protect this shared resource in a common codebase for both potential concurrency usages?

like image 538
Telastyn Avatar asked Jan 09 '14 03:01

Telastyn


People also ask

How do you protect shared resources from concurrent access?

For example, if we have a shared resource, and multiple threads want to access the shared resource, then we need to protect the shared resource from concurrent access otherwise we will get some inconsistent behavior or output. In C#, we can use Lock and Monitor to provide thread safety in a multithreaded application.

What is a multi threaded environment?

Multithreading is a Java feature that allows concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such program is called a thread. So, threads are light-weight processes within a process.

Is async multi threaded?

Asynchronous Programming vs Multithreading It is a general misconception that both asynchronous programming and multithreading are the same although that's not true. Asynchronous programming is about the asynchronous sequence of Tasks, while multithreading is about multiple threads running in parallel.

What resources are shared among different threads in a multithreaded process?

In a multi-threaded process, all of the process' threads share the same memory and open files. Within the shared memory, each thread gets its own stack. Each thread has its own instruction pointer and registers.


1 Answers

You can use SemaphoreSlim with 1 as the number of requests. SemaphoreSlim allows to lock in both an async fashion using WaitAsync and the old synchronous way:

await _semphore.WaitAsync()
try
{
    ... use shared resource.
}
finally
{
    _semphore.Release()
}

You can also write your own AsyncLock based on Stephen Toub's great post Building Async Coordination Primitives, Part 6: AsyncLock. I did it in my application and allowed for both synchronous and asynchronous locks on the same construct.

Usage:

// Async
using (await _asyncLock.LockAsync())
{
    ... use shared resource.
}

// Synchronous
using (_asyncLock.Lock())
{
    ... use shared resource.
}

Implementation:

class AsyncLock
{
    private readonly Task<IDisposable> _releaserTask;
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    private readonly IDisposable _releaser;

    public AsyncLock()
    {
        _releaser = new Releaser(_semaphore);
        _releaserTask = Task.FromResult(_releaser);
    }
    public IDisposable Lock()
    {
        _semaphore.Wait();
        return _releaser;
    }
    public Task<IDisposable> LockAsync()
    {
        var waitTask = _semaphore.WaitAsync();
        return waitTask.IsCompleted
            ? _releaserTask
            : waitTask.ContinueWith(
                (_, releaser) => (IDisposable) releaser,
                _releaser,
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously,
                TaskScheduler.Default);
    }
    private class Releaser : IDisposable
    {
        private readonly SemaphoreSlim _semaphore;
        public Releaser(SemaphoreSlim semaphore)
        {
            _semaphore = semaphore;
        }
        public void Dispose()
        {
            _semaphore.Release();
        }
    }
}
like image 89
i3arnon Avatar answered Oct 23 '22 19:10

i3arnon