主线程迭代之间的资源锁定(Async/Await)

2024-05-21

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

When button1单击后,其处理程序会进行一些修改r本身然后调用_IndependentResourceModifierAsync()异步进行一些修改r在生成的任务中。_IndependentResourceModifierAsync()获得r执行此操作之前先锁定。也因为处理程序搞乱了r它本身就获得了r的锁也。

When button2被点击,它只是调用_IndependentResourceModifierAsync()直接地。它本身不锁定。

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

我想保证两件事:

  1. 如果其中之一button1 or button2当资源被主线程锁定时点击,会抛出异常。 (不能使用Monitor or Mutex因为它们是线程驱动的)
  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();
            }
    }
}

资源有自己的锁定和解锁代码来处理并发性。任何线程都可以修改资源。

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

单击 Button1 时,其处理程序会对 r 本身进行一些修改,然后异步调用 _IndependentResourceModifierAsync(),这会在生成的任务中对 r 进行一些修改。 _IndependentResourceModifierAsync() 在执行此操作之前获取 r 的锁。另外,因为处理程序正在干扰 r 本身,所以它也获取了 r 的锁。

还有一个危险信号。递归锁几乎总是一个坏主意。我在博客上解释了我的推理。 http://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html

我还收到了有关设计的另一个警告:

如果在主线程锁定资源时单击button1或button2,则会引发异常。 (不能使用监视器或互斥体,因为它们是线程驱动的)

这对我来说听起来不对。还有其他方法可以做到这一点吗?随着状态变化禁用按钮似乎是一个更好的方法。


我强烈建议重构以消除锁递归的要求。然后你可以使用SemaphoreSlim with WaitAsync异步获取锁并Wait(0)对于“尝试锁定”。

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

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
}

我做了很多搜索,寻找其他遇到过这个问题的人,但都一无所获。这可能意味着我把事情过于复杂化了,但我很好奇人们对此有何看法。

在很长一段时间里,这是不可能的(完全是这样)。对于 .NET 4.5,这是可能的,但它并不完美。这非常复杂。我不知道anyone实际上在生产中这样做,我当然不推荐它。

也就是说,我一直在玩以异步递归锁为例 https://nitoasyncex.codeplex.com/SourceControl/changeset/view/cfd83ac30725#Source/AdvancedExamples/RecursiveAsyncLockExample.cs在我的 AsyncEx 库中(它永远不会成为公共 API 的一部分)。您可以像这样使用它(遵循已取消令牌同步操作的 AsyncEx 约定 http://blog.stephencleary.com/2013/04/cancellationtoken-behavior-with-asyncex.html):

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篇幅虽然不长,但是想想就让人心旷神怡。它始于隐式异步上下文 http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html我在我的博客上详细描述了它(它本身很难理解),然后使用自定义等待在正确的时间向最终用户“注入”代码async方法。

你正处于任何人都尝试过的边缘。RecursiveAsyncLock根本没有经过彻底的测试,而且很可能永远不会。

小心行事,探险家。这里有龙。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

主线程迭代之间的资源锁定(Async/Await) 的相关文章

随机推荐

  • 猫鼬不创建索引

    我最近开始使用 Mongoose v 3 2 1 但我遇到了索引问题 我在我的模式 Schema path attr index true 上定义了几个索引 但它们不是在数据库中创建的 我在 shell 中运行 db collection
  • Linux 上的用户空间能否实现本机代码的抢占式多任务处理?

    我想知道是否可以在 Linux 用户空间的单个进程中实现本机代码的抢占式多任务处理 也就是说 从外部暂停一些正在运行的本机代码 保存上下文 交换到不同的上下文 然后恢复执行 所有这些都由用户空间精心安排 但使用可能进入内核的调用 我认为这可
  • 更改 Android JellyBean 上的操作栏高度

    我最近一直在开发一个 Android 应用程序 其中我需要为选项卡栏提供自定义布局和尺寸 到目前为止 我所做的方法是使用 Jake Wharton 的 ActionBarSherlock 库来支持 HoneyComb 之前的 Android
  • Rails 3.2:ArgumentError:创建时参数数量错误(2 为 1)

    尝试创建模型的实例时 出现以下错误 u User create or where first or create or where first or initialize ArgumentError wrong number of argu
  • UWP 组合 - 将不透明蒙版应用到 ListView 的顶部 30 像素

    如何将效果应用到 ListView 其中顶部 30 像素从完全透明渐变为完全不透明 这个想法是 当你向下滚动时 顶部的项目逐渐消失 我正在构建一个 UWP 应用程序 其中设计要求 ListView 的顶部 30px 从不透明度 0 开始并过
  • C++ 输出中的微笑

    我现在正在编写 Tic Tac Toe 控制台游戏 我有一个非常有趣的问题 该程序运行良好 但问题出在 Tic Tac Toe 矩阵中 最后一个元素 最后一行 最后一列 是一个微笑 它有时会发生变化 有时是笑意 有时是一颗心 怎么了 整个代
  • React Context 的范围是什么?

    我不清楚在哪里Context可以在 React 应用程序中创建 我可以在组件树中的任何位置创建上下文吗 如果可以 该上下文的范围是否仅限于创建它的子级 或者 Context 本质上是全局的 我可以在文档中的哪里找到这个 案例 我在页面上多次
  • 如何将设备令牌和应用程序版本发送到服务器

    我已经实现将设备令牌和应用程序版本发送到 serverm 它在模拟器 硬编码数据 中工作正常 但在设备中无法工作 任何形式的帮助将不胜感激 先感谢您 这是代码 void application UIApplication applicati
  • WPF .exe - 大文件大小

    我正在开发一个 WPF 应用程序 发现 exe 大小超过 1 2MB 我想减少最终可执行文件的大小 代码不过200kb 我在项目中使用了一些 png图片 总共占用了20kb左右 为什么最终的可执行文件这么大 我使用 ILDASM 统计信息来
  • Flutter 和 Openlayers - 包括 flutter 中的 js 库

    来自基于 js cordova 的跨平台领域 我最近正在深入研究 flutter 所以我想知道直接使用 flutter 中的各种 js 库的选项 更具体地说 我有兴趣在 flutter 中使用 openlayers 库来创建具有各种绘图和编
  • 在Windows上安装php Composer时出现错误

    在安装 Composer 以使用 Laravel 框架时 我遇到了一些错误 Download failed file get contents SSL operation failed with code 1 OpenSSL Error m
  • 如何在控件内引用用户控件主机的 StaticResource?

    我有以下内容StaticResource in my Window Resources
  • 从 android apk 中的 .smali 文件中提取所有 API 调用

    我想从 Android 应用程序的 apk 中提取所有 API 调用 我已经使用 apktool 从 apk 中获取 smali 代码文件 我可以手动发现一些 API 调用 但我需要一种自动化方法 例如 我可以看到getNetworkInf
  • Phonegap使用命令行工具添加插件

    我是phonegap的新手 我按照phonegap官方网站中定义的步骤使用命令行工具创建项目 nodejs 我成功创建项目并添加平台 但是 当我尝试添加插件时出现以下错误 命令 cordova plugin add https git wi
  • 如何在 RDLC 报告上添加每个组的行号?

    我如何添加这样的行号 GROUP 1 行号 ID 姓名 年龄 1 231 test 43 2 324 test2 45 3 354 test3 34 GROUP 2 行号 ID 姓名 年龄 1 657 test4 43 2 534 test
  • Android 中的 AES 解密速度慢

    我尝试使用 AES 128 位密钥解密 4 2 MB dcf 文件 但解密需要 33 秒 在函数 cipher doFinal data 上 这正常吗 这是一个代码片段 long start System currentTimeMillis
  • 给定数十亿个 URL,如何确定重复内容 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我在一次编程面试中被问到这个问题 我在下面详细描述了这个问题 这是一个开放式问题 给定数十亿个 URL 深层链接 我如何对哪些 URL
  • 如何从基类捕获点击项目(位于模板中)的事件?

    我有一个基础网格
  • “Equals”和“SequenceEqual”之间的区别?

    是否存在以下情况 Equals MyList1 MyList2 MyList1 SequenceEqual MyList2 两者之间有什么区别 Equals obj1 obj2 and obj1 Equals obj2 Thanks Equ
  • 主线程迭代之间的资源锁定(Async/Await)

    假设我有一个带有两个按钮的表单 button1 and button2 和资源对象 r 资源有自己的锁定和解锁代码来处理并发性 任何线程都可以修改资源 When button1单击后 其处理程序会进行一些修改r本身然后调用 Independ