c#中的await关键字。NET Async CTP)不允许在锁语句中使用。

从MSDN:

一个 Await表达式不能用于同步函数或查询中 表达式,在异常处理的catch或finally块中 语句,在锁语句的块中,或在不安全上下文中。

我认为由于某些原因,编译器团队很难或不可能实现这一点。

我尝试使用using语句:

class Async
{
    public static async Task<IDisposable> Lock(object obj)
    {
        while (!Monitor.TryEnter(obj))
            await TaskEx.Yield();

        return new ExitDisposable(obj);
    }

    private class ExitDisposable : IDisposable
    {
        private readonly object obj;
        public ExitDisposable(object obj) { this.obj = obj; }
        public void Dispose() { Monitor.Exit(this.obj); }
    }
}

// example usage
using (await Async.Lock(padlock))
{
    await SomethingAsync();
}

然而,这并没有像预期的那样工作。对Monitor的调用。在ExitDisposable中退出。Dispose似乎无限期地阻塞(大多数时间),当其他线程试图获得锁时,会导致死锁。我怀疑我周围工作的不可靠性和原因等待语句不允许在锁语句以某种方式相关。

有人知道为什么在lock语句体中不允许使用await吗?


当前回答

这只是这个答案的延伸。

using System;
using System.Threading;
using System.Threading.Tasks;

public class SemaphoreLocker
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    public async Task LockAsync(Func<Task> worker)
    {
        await _semaphore.WaitAsync();
        try
        {
            await worker();
        }
        finally
        {
            _semaphore.Release();
        }
    }

    // overloading variant for non-void methods with return type (generic T)
    public async Task<T> LockAsync<T>(Func<Task<T>> worker)
    {
        await _semaphore.WaitAsync();
        try
        {
            return await worker();
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

用法:

public class Test
{
    private static readonly SemaphoreLocker _locker = new SemaphoreLocker();

    public async Task DoTest()
    {
        await _locker.LockAsync(async () =>
        {
            // [async] calls can be used within this block 
            // to handle a resource by one thread. 
        });
        // OR
        var result = await _locker.LockAsync(async () =>
        {
            // [async] calls can be used within this block 
            // to handle a resource by one thread. 
        });
    }
}

其他回答

这参考了构建异步协调原语,第6部分:AsyncLock, http://winrtstoragehelper.codeplex.com/, Windows 8应用程序商店和。net 4.5

以下是我的观点:

async/await语言特性使许多事情变得相当简单,但它也引入了一个场景 很少遇到以前如此容易使用异步调用:重新进入。

对于事件处理程序来说尤其如此,因为对于许多事件,您不知道从事件处理程序返回后发生了什么。 可能发生的一件事是,你在第一个事件处理程序中等待的异步方法,从另一个事件处理程序中调用 相同的线程。

Here is a real scenario I came across in a windows 8 App store app: My app has two frames: coming into and leaving from a frame I want to load/safe some data to file/storage. OnNavigatedTo/From events are used for the saving and loading. The saving and loading is done by some async utility function (like http://winrtstoragehelper.codeplex.com/). When navigating from frame 1 to frame 2 or in the other direction, the async load and safe operations are called and awaited. The event handlers become async returning void => they cant be awaited.

然而,该实用程序的第一个文件打开操作(让我们说:在保存函数内)也是异步的 因此,第一个await将控制返回给框架,框架稍后通过第二个事件处理程序调用另一个实用程序(load)。 加载现在尝试打开相同的文件,如果 文件现在已经打开用于保存操作,失败并出现ACCESSDENIED异常。

对我来说,一个最小的解决方案是通过using和AsyncLock来保护文件访问。

private static readonly AsyncLock m_lock = new AsyncLock();
...

using (await m_lock.LockAsync())
{
    file = await folder.GetFileAsync(fileName);
    IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
    using (Stream inStream = Task.Run(() => readStream.AsStreamForRead()).Result)
    {
        return (T)serializer.Deserialize(inStream);
    }
}

请注意,他的锁基本上只用一个锁就锁定了该实用程序的所有文件操作,这是不必要的强大,但在我的场景中工作得很好。

下面是我的测试项目:一个windows 8应用商店应用程序,对http://winrtstoragehelper.codeplex.com/的原始版本和使用Stephen Toub的AsyncLock的修改版本进行了一些测试调用。

我还可以推荐这个链接: http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx

嗯,看起来很丑,但似乎有用。

static class Async
{
    public static Task<IDisposable> Lock(object obj)
    {
        return TaskEx.Run(() =>
            {
                var resetEvent = ResetEventFor(obj);

                resetEvent.WaitOne();
                resetEvent.Reset();

                return new ExitDisposable(obj) as IDisposable;
            });
    }

    private static readonly IDictionary<object, WeakReference> ResetEventMap =
        new Dictionary<object, WeakReference>();

    private static ManualResetEvent ResetEventFor(object @lock)
    {
        if (!ResetEventMap.ContainsKey(@lock) ||
            !ResetEventMap[@lock].IsAlive)
        {
            ResetEventMap[@lock] =
                new WeakReference(new ManualResetEvent(true));
        }

        return ResetEventMap[@lock].Target as ManualResetEvent;
    }

    private static void CleanUp()
    {
        ResetEventMap.Where(kv => !kv.Value.IsAlive)
                     .ToList()
                     .ForEach(kv => ResetEventMap.Remove(kv));
    }

    private class ExitDisposable : IDisposable
    {
        private readonly object _lock;

        public ExitDisposable(object @lock)
        {
            _lock = @lock;
        }

        public void Dispose()
        {
            ResetEventFor(_lock).Set();
        }

        ~ExitDisposable()
        {
            CleanUp();
        }
    }
}

使用SemaphoreSlim。WaitAsync方法。

 await mySemaphoreSlim.WaitAsync();
 try {
     await Stuff();
 } finally {
     mySemaphoreSlim.Release();
 }

这只是这个答案的延伸。

using System;
using System.Threading;
using System.Threading.Tasks;

public class SemaphoreLocker
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    public async Task LockAsync(Func<Task> worker)
    {
        await _semaphore.WaitAsync();
        try
        {
            await worker();
        }
        finally
        {
            _semaphore.Release();
        }
    }

    // overloading variant for non-void methods with return type (generic T)
    public async Task<T> LockAsync<T>(Func<Task<T>> worker)
    {
        await _semaphore.WaitAsync();
        try
        {
            return await worker();
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

用法:

public class Test
{
    private static readonly SemaphoreLocker _locker = new SemaphoreLocker();

    public async Task DoTest()
    {
        await _locker.LockAsync(async () =>
        {
            // [async] calls can be used within this block 
            // to handle a resource by one thread. 
        });
        // OR
        var result = await _locker.LockAsync(async () =>
        {
            // [async] calls can be used within this block 
            // to handle a resource by one thread. 
        });
    }
}

我确实尝试使用一个监视器(下面的代码),它似乎可以工作,但有一个GOTCHA…当你有多个线程时,它会给出。对象同步方法从未同步的代码块调用。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
    public class ThreadsafeFooModifier : 
    {
        private readonly object _lockObject;

        public async Task<FooResponse> ModifyFooAsync()
        {
            FooResponse result;
            Monitor.Enter(_lockObject);
            try
            {
                result = await SomeFunctionToModifyFooAsync();
            }
            finally
            {
                Monitor.Exit(_lockObject);
            }
            return result;
        }
    }
}

在此之前,我只是简单地做这个,但它是在一个ASP。NET控制器,因此导致死锁。

public async task<FooResponse> modifyFooAsync() { 锁(锁对象) { 返回 SomeFunctionToModifyFooAsync.Result; } }