锁定语句中不允许使用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();
}
Run Code Online (Sandbox Code Playgroud)
但是这不能按预期工作.在ExitDisposable.Dispose中对Monitor.Exit的调用似乎无限期地(大部分时间)阻塞,导致死锁,因为其他线程试图获取锁.我怀疑我的工作不可靠以及锁定语句中不允许等待语句的原因在某种程度上是相关的.
有谁知道为什么在锁定声明的正文中不允许等待?
(问题修改):到目前为止,答案都包括一个线程重新进入锁定区域的线程,通过递归之类的东西,你可以跟踪单个线程进入锁定两次的步骤.但是有可能以某种方式,对于单个线程(可能来自ThreadPool,可能是由于定时器事件或异步事件或线程进入休眠状态并在其他一些代码块中单独唤醒/重用)以某种方式产生两个不同的地方彼此独立,因此,当开发人员通过简单地阅读他们自己的代码而没有想到它时,会遇到锁重入问题?
在ThreadPool类备注(单击此处)中,备注似乎表明睡眠线程应在不使用时重复使用,否则会因睡眠而浪费.
但是在Monitor.Enter参考页面上(点击这里),他们说"同一个线程在没有阻止的情况下不止一次调用Enter是合法的". 所以我认为必须有一些我应该小心避免的东西.它是什么?这怎么可能单个线程输入两次相同的锁定区域?
假设您有一些锁定区域,不幸的是很长时间.这可能是现实的,例如,如果您访问已被分页的内存(或其他内容).锁定区域中的线程可能会进入睡眠状态.同一个线程是否有资格运行更多代码,这可能会意外地进入同一个锁定区域?在我的测试中,以下内容不会使同一个线程的多个实例运行到同一个锁定区域.
那怎么产生问题呢?你究竟需要小心避免什么?
class myClass
{
private object myLockObject;
public myClass()
{
this.myLockObject = new object();
int[] myIntArray = new int[100]; // Just create a bunch of things so I may easily launch a bunch of Parallel things
Array.Clear(myIntArray, 0, myIntArray.Length); // Just create a bunch of things so I may easily launch a bunch of Parallel things
Parallel.ForEach<int>(myIntArray, i => MyParallelMethod());
}
private void MyParallelMethod()
{
lock (this.myLockObject)
{ …
Run Code Online (Sandbox Code Playgroud) 我发现构建异步协调基元,第 1 部分:AsyncManualResetEvent可能与我的主题相关。
对于 TaskCompletionSource,这意味着同步延续可以作为调用 {Try}Set* 的一部分发生,这意味着在我们的 AsyncManualResetEvent 示例中,这些延续可以作为 Set 方法的一部分执行。根据您的需求(以及 Set 的调用者是否可以接受在所有同步延续执行时可能运行更长时间的 Set 调用),这可能是也可能不是您想要的。
非常感谢所有的答案,感谢您的知识和耐心!
我知道它Task.Run
在线程池线程上运行,并且线程可以重入。但我从来不知道两个任务可以在同一个线程上运行,当它们都处于活动状态时!
我的问题是:设计合理吗?这是否意味着lock
异步方法内部毫无意义(或者说,lock
如果我想要一个不允许重入的方法,则在异步方法块中不能被信任)?
代码:
namespace TaskHijacking
{
class Program
{
static TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
static object methodLock = new object();
static void MethodNotAllowReetrance(string callerName)
{
lock(methodLock)
{
Console.WriteLine($"Enter MethodNotAllowReetrance, caller: {callerName}, on thread: {Thread.CurrentThread.ManagedThreadId}");
if (callerName == "task1")
{
tcs.SetException(new Exception("Terminate tcs"));
}
Thread.Sleep(1000);
Console.WriteLine($"Exit MethodNotAllowReetrance, caller: {callerName}, on …
Run Code Online (Sandbox Code Playgroud)