在异步方法

时间:2017-07-20 12:08:37

标签: c# multithreading async-await thread-safety locking

我有MVC应用程序,其中必须按特定顺序执行操作方法。最近,我遇到了一些奇怪的问题,我假设这是因为我没有进行任何线程同步。我几乎没有使用过多线程,我对它知之甚少。我试图实现某种锁定,我必须根据Id锁定。所以我实现了如下所示的类来获取所需的锁定对象。

public class ReportLockProvider
    : IReportLockProvider
{
    readonly ConcurrentDictionary<long, object> lockDictionary 
        = new ConcurrentDictionary<long, object>();

    public object ProvideLockObject(long reportId)
    {
        return lockDictionary.GetOrAdd(reportId, new object());
    }
}

我尝试使用如下:

ReportLockProvider lockProvider = new ReportLockProvider();

public async ActionResult MyAction(long reportId)
{
    lock(lockProvider.ProvideLockObject(reportId))
    {
        // Some operations
        await Something();
        // Some operation
    }
}

我希望它会起作用,但事件没有编译,因为我在await体内使用了lock。我搜索了一下,然后在this answer找到了SemaphoreSlim。现在,问题是我必须根据Id 获取锁定对象。我怎样才能做到这一点?可以创建多个SemaphoreSlim个对象吗? 如果我修改下面的代码

,可以
public class ReportLockProvider
    : IReportLockProvider
{
    readonly ConcurrentDictionary<long, SemaphoreSlim> lockDictionary 
        = new ConcurrentDictionary<long, SemaphoreSlim>();

    public SemaphoreSlim ProvideLockObject(long reportId)
    {
        return lockDictionary.GetOrAdd(reportId, new SemaphoreSlim(1, 1));
    }
}


public async ActionResult MyAction(long reportId)
{
    var lockObject = ReportLockProvider.ProvideLockObject(reportId);

    await lockObject.WaitAsync();
    try
    {
        // Some operations
        await Something();
        // Some operation
    }
    finally
    {
        lockObject.Release();
    }
}

另一个问题是,我可以在非异步方法中使用SemaphoreSlim吗? 还有更好的选择吗?

1 个答案:

答案 0 :(得分:0)

我认为你在lockDictionary面前缺少一个静态关键字,但这取决于你如何实现提供者。

这是一个示例,其中包含我在LinqPad中编写的一些更改代码:

async Task Main()
{
    ReportLockProvider reportLockProvider = new ReportLockProvider();
    List<Task> tasks = new List<Task>(10);

    for (long i = 1; i <= 5; i++) {
        var local = i;
        tasks.Add(Task.Run(() => Enter(local) ));
        tasks.Add(Task.Run(() => Enter(local) ));
    }

    async Task Enter(long id)
    {
        Console.WriteLine(id + " waiting to enter");
        await reportLockProvider.WaitAsync(id);

        Console.WriteLine(id + " entered!");
        Thread.Sleep(1000 * (int)id);

        Console.WriteLine(id + " releasing");
        reportLockProvider.Release(id);
    }

    await Task.WhenAll(tasks.ToArray());
}

public class ReportLockProvider
{
    static readonly ConcurrentDictionary<long, SemaphoreSlim> lockDictionary = new ConcurrentDictionary<long, SemaphoreSlim>();

    public async Task WaitAsync(long reportId)
    {
        await lockDictionary.GetOrAdd(reportId, new SemaphoreSlim(1, 1)).WaitAsync();
    }
    public void Release(long reportId)
    {
        SemaphoreSlim semaphore;
        if (lockDictionary.TryGetValue(reportId, out semaphore))
        {
            semaphore.Release();
        }
    }
}