gpt4 book ai didi

c# - 主线程迭代之间的资源锁定(异步/等待)

转载 作者:行者123 更新时间:2023-11-30 19:10:42 24 4
gpt4 key购买 nike

假设我有一个带有两个按钮(button1button2)和一个资源对象(r)的表单。资源有自己的锁定和解锁代码来处理并发。资源可以被任何线程修改。

当点击 button1 时,它的处理程序对 r 本身进行一些修改,然后异步调用 _IndependentResourceModifierAsync() 进行一些修改code>r 在生成的任务中。 _IndependentResourceModifierAsync() 在执行此操作之前获取 r 的锁。此外,由于处理程序与 r 本身发生冲突,它也获得了 r 的锁。

button2被点击时,它只是直接调用_IndependentResourceModifierAsync()。它不会自行锁定。

如您所知,按钮的处理程序将始终在主线程上执行(生成的 Task 除外)。

有两件事我想保证:

  1. 如果在资源被主线程锁定时单击button1button2,将抛出异常。 (不能使用 MonitorMutex 因为它们是线程驱动的)
  2. button1_Click()_IndependentResourceModiferAsync() 的锁嵌套不应导致死锁。 (不能使用 Semaphore)。

基本上,我认为我正在寻找的是“基于堆栈的锁”,如果这样的事情存在或什至可能的话。因为当异步方法在等待之后继续时,它会恢复堆栈状态。我做了很多搜索,寻找其他遇到过这个问题但没有找到的人。这可能意味着我把事情复杂化了,但我很好奇人们对此有何看法。可能有一些非常明显的东西我失踪了。非常感谢。

public class Resource
{
public bool TryLock();
public void Lock();
public void Unlock();
...
}

public class MainForm : Form
{
private Resource r;
private async void button1_Click(object sender, EventArgs e)
{
if (!r.TryLock())
throw InvalidOperationException("Resource already acquired");
try
{
//Mess with r here... then call another procedure that messes with r independently.
await _IndependentResourceModiferAsync();
}
finally
{
r.Unlock();
}
}

private async void button2_Click(object sender, EventArgs e)
{
await _IndependentResourceModifierAsync();
}

private async void _IndependentResourceModiferAsync()
{
//This procedure needs to check the lock too because he can be called independently
if (!r.TryLock())
throw InvalidOperationException("Resource already acquired");
try
{
await Task.Factory.StartNew(new Action(() => {
// Mess around with R for a long time.
}));
}
finally
{
r.Unlock();
}
}
}

最佳答案

The resource has its own locking and unlocking code to handle concurrency. The resource could be modified by any thread.

有一面黄旗。我发现从长远来看,保护资源(而不是让它们保护自己)的设计通常更好。

When button1 is clicked, its handler does some modifying of r itself and then calls _IndependentResourceModifierAsync() asynchronously which does some modifying of r in a spawned task. _IndependentResourceModifierAsync() acquires r's lock before doing this. Also because the handler is messing with r itself, it acquires r's lock too.

还有一面红旗。递归锁几乎总是一个坏主意。 I explain my reasoning on my blog.

还有另一个关于设计的警告:

If either button1 or button2 is clicked while the resource is locked by the main thread, an exception will be thrown. (Can't use a Monitor or Mutex because they are thread driven)

这听起来不对。还有其他方法吗?在状态更改时禁用按钮似乎是一种更好的方法。


我强烈建议重构以消除对锁递归的要求。然后您可以使用 SemaphoreSlimWaitAsync 异步获取锁和 Wait(0) 以获取“try-lock”。

所以你的代码最终会看起来像这样:

class Resource
{
private readonly SemaphoreSlim mutex = new SemaphoreSlim(1);

// Take the lock immediately, throwing an exception if it isn't available.
public IDisposable ImmediateLock()
{
if (!mutex.Wait(0))
throw new InvalidOperationException("Cannot acquire resource");
return new AnonymousDisposable(() => mutex.Release());
}

// Take the lock asynchronously.
public async Task<IDisposable> LockAsync()
{
await mutex.WaitAsync();
return new AnonymousDisposable(() => mutex.Release());
}
}

async void button1Click(..)
{
using (r.ImmediateLock())
{
... // mess with r
await _IndependentResourceModiferUnsafeAsync();
}
}

async void button2Click(..)
{
using (r.ImmediateLock())
{
await _IndependentResourceModiferUnsafeAsync();
}
}

async Task _IndependentResourceModiferAsync()
{
using (await r.LockAsync())
{
await _IndependentResourceModiferUnsafeAsync();
}
}

async Task _IndependentResourceModiferUnsafeAsync()
{
... // code here assumes it owns the resource lock
}

I did a lot of searching for anyone else who has had this problem but came up dry. That likely means I'm over-complicating things, but I am curious what people have to say about it.

很长一段时间,这是不可能的(根本不可能,句号,句号)。在 .NET 4.5 中,这是可能的,但并不完美。这很复杂。我不知道有人在生产中实际这样做,我当然不推荐它。

就是说,我一直在玩弄 asynchronous recursive locks as an example在我的 AsyncEx 库中(它永远不会成为公共(public) API 的一部分)。您可以像这样使用它(在 AsyncEx convention of already-cancelled tokens acting synchronously 之后):

class Resource
{
private readonly RecursiveAsyncLock mutex = new RecursiveAsyncLock();
public RecursiveLockAsync.RecursiveLockAwaitable LockAsync(bool immediate = false)
{
if (immediate)
return mutex.LockAsync(new CancellationToken(true));
return mutex.LockAsync();
}
}

async void button1Click(..)
{
using (r.LockAsync(true))
{
... // mess with r
await _IndependentResourceModiferAsync();
}
}

async void button2Click(..)
{
using (r.LockAsync(true))
{
await _IndependentResourceModiferAsync();
}
}

async Task _IndependentResourceModiferAsync()
{
using (await r.LockAsync())
{
...
}
}

RecursiveAsyncLock 的代码不是很长,但想想就让人费解。它以 implicit async context 开头我在我的博客上详细描述了它(仅靠它本身很难理解),然后使用自定义 awaitables 在最终用户 async 方法的正确时间“注入(inject)”代码。

您正处于任何人试验过的事物的边缘。 RecursiveAsyncLock 根本没有经过全面测试,而且可能永远不会。

小心行事,探索者。这里有龙。

关于c# - 主线程迭代之间的资源锁定(异步/等待),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16193692/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com