Kev*_*vin 318 .net c# async-await
锁定语句中不允许使用C#(.NET Async CTP)中的await关键字.
来自MSDN:
await表达式不能用于同步函数,查询表达式,异常处理语句的catch或finally块,lock语句块或不安全上下文中.
我认为编译器团队出于某种原因要么难以实施,要么难以实现.
我尝试使用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();
}
但是这不能按预期工作.在ExitDisposable.Dispose中对Monitor.Exit的调用似乎无限期地(大部分时间)阻塞,导致死锁,因为其他线程试图获取锁.我怀疑我的工作不可靠以及锁定语句中不允许等待语句的原因在某种程度上是相关的.
有谁知道为什么在锁定声明的正文中不允许等待?
Eri*_*ert 340
我认为编译器团队出于某种原因要么难以实施,要么难以实现.
不,实现起来并不困难或不可能 - 您自己实施它的事实证明了这一事实.相反,这是一个非常糟糕的主意,因此我们不允许它,以保护您免于犯这个错误.
在ExitDisposable.Dispose中调用Monitor.Exit似乎无限期地(大部分时间)阻塞,导致死锁,因为其他线程试图获取锁.我怀疑我的工作不可靠以及锁定语句中不允许等待语句的原因在某种程度上是相关的.
正确的,你已经发现了我们为什么把它变成非法的.在锁内等待是一个产生死锁的方法.
我相信你可以理解为什么:在await将控制权返回给调用者并且方法恢复之间运行任意代码.任意代码可能会取出产生锁定顺序反转的锁,从而产生死锁.
更糟糕的是,代码可以在另一个线程上恢复(在高级方案中;通常你会在执行等待的线程上再次获取,但不一定),在这种情况下,解锁将解锁与不同线程上的锁相比出锁.这是一个好主意吗?没有.
我注意到,它也是一个"最差实践"做一个yield return内部的lock,出于同样的原因.这样做是合法的,但我希望我们把它变成非法的.我们不会为"等待"犯同样的错误.
小智 258
 await mySemaphoreSlim.WaitAsync();
 try {
     await Stuff();
 } finally {
     mySemaphoreSlim.Release();
 }
Jon*_*eet 64
基本上这样做是错误的.
有两种方法可以实现:
保持锁定,只在块的末端释放它.
这是一个非常糟糕的主意,因为你不知道异步操作需要多长时间.你应该只持有最少的时间锁.它也可能是不可能的,因为一个线程拥有一个锁,而不是一个方法 - 你甚至可能不会在同一个线程上执行其余的异步方法(取决于任务调度程序).
在await中释放锁,并在await返回时重新获取它
 
这违反了最不惊讶的原则IMO,其中异步方法应该像等效的同步代码一样尽可能地行事 - 除非你Monitor.Wait在锁定块中使用,你期望在块的持续时间内拥有锁.
所以基本上这里有两个相互竞争的要求 - 你不应该在这里尝试第一个,如果你想采用第二种方法,你可以通过将两个分开的锁定块用await表达式分隔来使代码更清晰:
// Now it's clear where the locks will be acquired and released
lock (foo)
{
}
var result = await something;
lock (foo)
{
}
因此,通过禁止您等待锁定块本身,该语言迫使您思考您真正想要做的事情,并在您编写的代码中更清楚地做出选择.
Ser*_*gey 26
这只是这个答案的延伸.
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();
        }
    }
}
用法:
public class Test
{
    private static readonly SemaphoreLocker _locker = new SemaphoreLocker();
    public async Task DoTest()
    {
        await _locker.LockAsync(async () =>
        {
            // [asyn] calls can be used within this block 
            // to handle a resource by one thread. 
        });
    }
}
han*_*ans 16
这referes到http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx,http://winrtstoragehelper.codeplex.com/ ,Windows 8应用商店和.NET 4.5
这是我对此的看法:
异步/等待语言功能使很多事情变得相当容易,但它也引入了一个在使用异步调用之前很少遇到的场景:重入.
对于事件处理程序尤其如此,因为对于许多事件,您没有任何关于从事件处理程序返回后发生的事情的线索.实际可能发生的一件事是,您在第一个事件处理程序中等待的异步方法从另一个仍在同一线程上的事件处理程序调用.
这是我在Windows 8 App Store应用程序中遇到的一个真实场景:我的应用程序有两个框架:进入和离开框架我想加载/保护一些数据到文件/存储.OnNavigatedTo/From事件用于保存和加载.保存和加载由一些异步实用程序功能(如http://winrtstoragehelper.codeplex.com/)完成.当从第1帧导航到第2帧或在另一个方向上导航时,将调用并等待异步加载和安全操作.事件处理程序变为异步返回void =>他们无法等待.
但是,该实用程序的第一个文件打开操作(让我们说:在保存函数内)也是异步的,因此第一个await将控制权返回给框架,该框架稍后通过第二个事件处理程序调用另一个实用程序(加载).加载现在尝试打开同一个文件,如果文件现在已打开以进行保存操作,则会因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 http://blogs.msdn的AsyncLock的修改版本. com/b/pfxteam/archive/2012/02/12/10266988.aspx.
我还建议这个链接:http: //www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx
Stephen Taub已经实现了这个问题的解决方案,请参阅构建异步协调基元,第7部分:AsyncReaderWriterLock.
Stephen Taub在业界备受推崇,所以他所写的任何内容都可能是稳固的.
我不会重现他在博客上发布的代码,但我将展示如何使用它:
/// <summary>
///     Demo class for reader/writer lock that supports async/await.
///     For source, see Stephen Taub's brilliant article, "Building Async Coordination
///     Primitives, Part 7: AsyncReaderWriterLock".
/// </summary>
public class AsyncReaderWriterLockDemo
{
    private readonly IAsyncReaderWriterLock _lock = new AsyncReaderWriterLock(); 
    public async void DemoCode()
    {           
        using(var releaser = await _lock.ReaderLockAsync()) 
        { 
            // Insert reads here.
            // Multiple readers can access the lock simultaneously.
        }
        using (var releaser = await _lock.WriterLockAsync())
        {
            // Insert writes here.
            // If a writer is in progress, then readers are blocked.
        }
    }
}
如果您是一个融入.NET框架的方法,请SemaphoreSlim.WaitAsync改用.您将无法获得读取器/写入器锁定,但您将获得经过测试和测试的实现.
| 归档时间: | 
 | 
| 查看次数: | 88262 次 | 
| 最近记录: |