【问题标题】:Return thread to ThreadPool on lock在锁定时将线程返回到 ThreadPool
【发布时间】:2017-02-09 20:06:01
【问题描述】:

当我像这样锁定 ThreadPool 上的线程时,线程被阻塞:

private static object _testServerLock = new object();
private static TestServer _testServer = null;

public TestServer GetServer()
{
  lock (_testServerLock)
  {
    if (_testServer == null)
    {
      _testServer = new TestServer(); // does some async stuff internally
    }
  }
  return _testServer;
}

如果调用它的并发线程多于 ThreadPool 中的线程,所有线程最终都会等待锁定,而其他地方发生的异步代码无法继续,因为它正在等待 ThreadPool 中的空闲线程.

所以我不想阻塞线程,我需要在等待时将其返回到 ThreadPool。
是否有其他方法可以锁定将等待线程返回到 ThreadPool?

【问题讨论】:

  • 您考虑过使用 xUnit 的“类夹具”吗? xunit.github.io/docs/shared-context.html 您可以使用它与测试类中的所有测试共享 TestServer 实例。或者如果您想跨测试类共享上下文,也可以选择共享上下文
  • xunit 将默认为每个测试类创建一个夹具,这是我的夹具共享的静态资源,它应该只存在一次。而且我想并行运行测试,否则这将需要很长时间。
  • xUnit 根本不直接提供类似的东西(这是一般花瓶中一团糟的秘诀)。为了引起足够的并行性,下面指出了方法-您需要足够的并发测试类[不受同一个测试集合的约束],然后将asyncness 从资源传播到测试方法签名,方法是使每个一个Task<T>,但没有任何Waits

标签: asp.net-core locking threadpool xunit.net


【解决方案1】:

任何必须在锁内完成的事情都应该移到一个任务中,该任务在测试之前启动,并在它创建资源时完成。

每当测试想要获取任务创建的资源时,它可以在访问资源之前在创建者任务上等待等待。所以对资源的所有访问都是在任务中,不能阻塞池中的所有线程。

类似:

private static object _testServerLock = new object();
private static TestServer _testServer = null;
private static Task _testTask = null;

private async Task<TestServer> CreateTestServerAsync()
{
  ...
}

// Constructor of the fixture
public TestFixture()
{
  // The lock here may be ok, because it's before all the async stuff
  // and it doesn't wait for something inside
  lock (_testServerLock)
  {
    if (_testTask == null)
    {
      _testTask = Task.Run(async () => {
        // it's better to expose the async nature of the call
        _testServer = await CreateTestServerAsync();
      });
      // or just, whatever works
      //_testTask = Task.Run(() => {
      //  _testServer = new TestServer();
      //});
    }
  }
}

public async Task<TestServer> GetServerAsync()
{
  await _testTask;
  return _testServer;
}

更新:

您可以使用静态成员的初始化来解除锁定。

private static TestServer _testServer = null;
private static Task _testTask = Task.Run(async () => {
  _testServer = await CreateTestServerAsync();
});

private static async Task<TestServer> CreateTestServerAsync()
{
  ...
}

public TestFixture()
{
}

public async Task<TestServer> GetServerAsync()
{
  await _testTask;
  return _testServer;
}

【讨论】:

    【解决方案2】:

    使用 xUnit ~1.7+,您可以做的主要事情是让您的测试方法返回 Task&lt;T&gt;,然后使用 async/await,这将限制您对线程的硬阻塞/占用

    xUnit 2.0 + 具有并行执行和mechanism for controlling access to state to be shared among tests。但是请注意,这基本上是通过在测试类中一次运行一个测试并一次将 Class Fixture 分配给一个(这相当于通常发生的情况 - 每个类只运行一个测试方法)一次)。 (如果您使用 Collection Fixture,则集合中的所有测试类实际上都变成了一个单独的测试类。

    最后,xUnit 2 提供了开关来控制是否:

    • 程序集与其他 [程序集] 并行运行
    • 测试集合/测试类与其他类并行运行
    • 上一个

    您应该能够通过不隐藏asyncness 来管理您的问题,而是将其暴露给测试方法或通过IAsyncLifetime 进行构建/拆卸

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-20
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 2018-02-28
      相关资源
      最近更新 更多