C# 为什么同一(UI)线程内异步锁的顺序调用不';不同线程的工作方式不同(比如通过Task.Run)?
部分地,这个问题与有点相似,但是由于另一个问题没有被正确地问到(也没有被完全问到),我试着一般地问它,所以这个问题不能被认为是重复的 问题在于理解异步锁实际上是如何工作的。(在此上下文中,我指的是库,但是,我认为它在异步执行中使用通用方法)。 所以。例如,我们有一个主线程(让它成为UI线程): 因此,连续按Enter键每次启动C# 为什么同一(UI)线程内异步锁的顺序调用不';不同线程的工作方式不同(比如通过Task.Run)?,c#,async-await,locking,C#,Async Await,Locking,部分地,这个问题与有点相似,但是由于另一个问题没有被正确地问到(也没有被完全问到),我试着一般地问它,所以这个问题不能被认为是重复的 问题在于理解异步锁实际上是如何工作的。(在此上下文中,我指的是库,但是,我认为它在异步执行中使用通用方法)。 所以。例如,我们有一个主线程(让它成为UI线程): 因此,连续按Enter键每次启动doJob,而无需等待上一个作业完成 但是,当我们将其更改为: Task.Run(() => { doJob(); });
doJob
,而无需等待上一个作业完成
但是,当我们将其更改为:
Task.Run(() =>
{
doJob();
});
。。。每件事都像一个魔咒,在上一个任务完成之前,不会运行新的任务
很明显,异步逻辑与经典的lock(_myLock)
有很大的不同,并且无法直接比较,但是,为什么第一种方法不能以这种方式工作当第二次调用LockAsync将“锁定”(同样,在异步上下文中)开始直到前一次完成的“持久作业”
实际上,我有一个实际的要求,就是为什么我需要代码以这种方式工作(真正的问题是我如何使用await LockAsync实现这一点?): 例如,在我的应用程序(例如,我的移动应用程序)中,在每次启动时,有一些数据我正在开始预加载(这是一项公共服务,需要将这些数据保存在缓存中以供进一步使用),然后,当UI启动时,特定页面请求相同的数据以显示UI,并请求相同的服务加载相同的数据。因此,在没有任何自定义逻辑的情况下,服务将启动两个长期作业,以检索相同的数据包。相反,我希望我的UI在数据预加载完成后立即从缓存接收数据。 这样(一个抽象的可能场景):
classmyapp
{
字符串[]_cache=null;
AsyncLock _lock=新的AsyncLock();
异步任务LoadData()
{
使用(wait_lock.LockAsync())
{
如果(_cache==null)
{
等待任务延迟(时间跨度从秒(10));
_cache=new[]{“一”、“二”、“三”};
}
返回缓存;
}
}
void OnAppLaunch()
{
LoadData();
}
异步void OnMyCustomEvent()
{
var data=等待加载数据();
//用这些数据做些别的事情
}
}
如果我将它改为
Task.Run(async()=>{var data=await LoadData();})
这个问题就会解决,但它看起来不是非常干净和好的方法。正如Matthew在评论中指出的那样,AsyncLock
是可重入的,这意味着如果同一线程再次尝试获取锁,它认识到这一点,并允许它继续下去。AsyncLock
的作者写了一篇长篇文章,讲述了他写这篇文章的真正原因是什么:
这不是一只虫子;这是一个特色。™
在“更新5/25/2017”标题之后,有一些代码示例准确地展示了您在这里的体验,并展示了它是如何成为一项功能的
希望重新进入的原因有:
我怀疑这是因为你的锁是线程重新进入的——也就是说,如果试图获取锁的线程已经持有锁,那么它就被允许通过。只会阻止不同的线程。当您执行
Task.Run()
时,每次都有不同的线程试图获取锁,因此它被阻止。您是否检查了类?它可能是一个很好的替代方法,它公开了异步WaitAsync
方法,并将initialCount
设置为1,可以实现所需的锁定行为。@MatthewWatson,没错。这是因为“重新进入”,这是图书馆文件中提到的。然而,这就是为什么我在这里提出这个问题。为什么?为什么允许重新进入?我其实不需要这个,这就是造成我问题的原因。为了简洁起见,你是否省略了在finally块中释放信号量?谢谢@GabrielLuci。不幸的是,由于某种原因,我之前没有在谷歌上找到这篇文章。很好,你提到了。是的,在我的例子中,SemaphoreSlim正是我们所需要的!
Task.Run(() =>
{
doJob();
});
class MyApp
{
string[] _cache = null;
AsyncLock _lock = new AsyncLock();
async Task<IEnumerable<string>> LoadData()
{
using (await _lock.LockAsync())
{
if (_cache == null)
{
await Task.Delay(TimeSpan.FromSeconds(10));
_cache = new[] {"one", "two", "three"};
}
return _cache;
}
}
void OnAppLaunch()
{
LoadData();
}
async void OnMyCustomEvent()
{
var data = await LoadData();
// to do something else with the data
}
}
var lck = new SemaphoreSlim(1);
var doJob = new Action(async () => {
await lck.WaitAsync();
try {
// long lasting job
Console.WriteLine("+++ Job starts");
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("--- Job finished");
} finally {
lck.Release();
}
});