【问题标题】:Synchronising an Asynchronous call in c#在 C# 中同步异步调用
【发布时间】:2009-03-16 07:38:40
【问题描述】:

我在工作中的一个项目中遇到了相当尴尬的困境。我们需要在 4 或 5 个不同的服务中创建用户,并以这样一种方式进行设置,即如果一个失败,它们都会失败。它们被封装在一个事务范围块中。

我们需要添加用户的服务之一需要远程登录并伪造一些数据。还有其他方法可以做到(这需要花钱),但现在这就是我们所坚持的。添加一名用户大约需要 3 分钟。正如可以想象的那样,我们将努力显着降低这一点,但这并不是重点。此调用是异步的,并且必须能够正常工作。关键是,该服务最多只能有 10 个连接。

我们的项目正在设置为批量创建用户。因此,一次可能会创建 50 个用户。当只能通过 telnet 建立 10 个连接时,这会带来一个问题,并且除了 telnet 服务之外,处理的用户不太可能花费很长时间。我现在需要同步这个过程,以便在它完成之前其余的不能继续。

我们使用带有异步调用的回调和委托来实现该功能。封装异步部分的最佳方法是什么,直到它完成才继续?

我们是否应该设置一个仅在调用完成时终止的循环?线程库中有什么可以提供帮助的吗?我以前从未使用过线程,所以这对我来说是第一次。有什么工具可以帮助解决这个问题?

编辑:

如果我使用 BeginInvoke / EndInvoke 模式,第一个委托中的异步调用也会尊重开始/结束吗?

例子:

public void dele1(string message) {
    Console.Write(message);
    delegate2 del2 = new delegate2;
    del2();
    Console.Write("End of Delegate 2");
}

public void dele2() {
    // Long Processing
    Console.Write("Delegate 2");
}

public delegate void delegate1(String message);
public delegate void delegate2();

delegate1 del1 = new delegate1(dele1);
del1("Delegate 1").BeginInvoke;
del1().EndInvoke;
Console.Write("End of Delegate 1");

// 预期输出(End Invoke 等到 Delegate 2 完成):

Delegate 1
End of Delegate 2
Delegate 2
End of Delegate 1

// 或者(End Invoke 只等待委托 1 完成,而不是任何内部委托调用):

Delegate 1
End of Delegate 2
End of Delegate 1
Delegate 2

是否会等到第二个委托也完成处理才结束调用?还是我需要在所有委托调用中使用调用模式?

【问题讨论】:

  • 不确定您编辑的示例的语法。您能否进一步说明 del1 发生了什么?

标签: c# multithreading asynchronous delegates


【解决方案1】:

您确实可以使用监视器、信号量,或者您甚至可以旋转等待,直到您的异步方法调用完成。

但您也可以免费获得。如果您在之前以 BeginInvoke() 启动的委托上调用 EndInvoke(),则会阻塞直到异步工作完成。

不确定这是否有帮助,因为您必须使用这种异步模式。如果是这样,您将免费获得异步执行(以及从异步调用回同步调用的转换)。

查看Calling Synchronous Methods Asynchronously on MSDN 了解有关此模式的更多信息。

我希望这会有所帮助!

【讨论】:

  • 这听起来正是我需要的!谢谢老兄。
【解决方案2】:

听起来你想要一个队列...如果有一个正在进行的请求,添加到队列中(使用Monitor);通话完成后,检查队列...

【讨论】:

    【解决方案3】:

    IAsyncResult 有一个属性AsyncWaitHandle,它会为您提供一个等待句柄(如果有的话),该句柄将在操作完成时发出信号。

    因此,您可以使用WaitHandle.WaitAll(或.WaitAny)同时对多个句柄执行非旋转等待。

    【讨论】:

      【解决方案4】:

      您有几个选择。这里有几个想法-

      首先,您可以使用ManualResetEvent 来“阻塞”主线程,直到您的异步操作全部完成。这个想法是让主线程调用你的函数,然后执行 event.WaitOne(),函数负责设置事件。

      或者,您可以尝试使用Semaphore 之类的东西。它旨在帮助解决这些情况,因为您可以将其限制为同时发生 10 次,而不必担心自己尝试处理它。这可能是我的首选方法。

      【讨论】:

        【解决方案5】:

        使用线程有两个原因:

        • 充分利用 CPU 资源,从而加快速度
        • 将多个同步状态机编写为简单的方法,使它们更具可读性

        使用线程的缺点是:

        • 真的很难

        如果瓶颈是在远程机器上等待 3 分钟,那么值得注意的是,多线程不会为您带来任何性能优势。您可以编写整个单线程,维护一组最多十个“状态机”对象,并在用户创建的各个阶段移动它们,所有这些都来自一个线程,总体性能几乎没有差异。

        因此,您可能正在寻找可读性好的代码布局,通过将用户创建操作变成一系列调用,这些调用读起来像单个方法(如果您将它们作为线程运行,它们就是这样)。

        另一种方法是通过迭代器方法,即包含yield return 语句,这也为您提供了单方法可读性的优势。我上周一定有linked to this three times!这是 Jeffrey Richter 关于 AsyncEnumerator 的文章。

        【讨论】:

          【解决方案6】:

          我有点不清楚你是如何使用 TransactionScope 的(所以我可能会偏离基础),但你可以创建可以分配给工作线程的 DependentTransactions。在所有 DependentTransaction 成功提交之前,您的顶级事务将不会提交,反之亦然。只要您调用的操作封装了提交/回滚功能(IEnlistmentNotification 等),我发现这是一种跨线程执行全部或不执行任何操作的简单方法。

          【讨论】:

          • 你是对的,如果我们不限于最终过程的最大 10 个连接,这会很好。不幸的是,我们被限制为 10 个(实际上我们允许 2 个),因此按顺序完成所有这些操作比保持队列更容易。
          • 有限的并发连接......总是一个有趣的要求;)
          猜你喜欢
          • 1970-01-01
          • 2012-04-05
          • 2012-07-25
          • 1970-01-01
          • 2015-12-14
          • 1970-01-01
          • 2013-04-01
          • 1970-01-01
          • 2016-03-24
          相关资源
          最近更新 更多