没有EndInvoke的C#异步调用?

以下面的课程为例。

public class A { // ... void Foo(S myStruct){...} } public class B { public A test; // ... void Bar() { S myStruct = new S(); test.Foo(myStruct); } } 

现在,我希望方法调用test.Foo(myStruct)是一个异步调用(’fire-and-forget’)。 条形方法需要尽快返回。 代理,BeginInvoke,EndInvoke,ThreadPool等文档不能帮助我找到解决方案。

这是有效的解决方案吗?

  // Is using the `EndInvoke` method as the callback delegate valid? foo.BeginInvoke(myStruct, foo.EndInvoke, null); 

您无需致电EndInvoke ; 不称它仅仅意味着:

  • 您没有从方法中获得返回值。
  • 在方法执行期间抛出的任何exception都将消失。

听起来你想要“发射并忘记”,所以最简单的方法是使用匿名委托,例如:

 var del = new Action(foo.Bar); del.BeginInvoke(iar => { try { del.EndInvoke(iar); } catch (Exception ex) { // Log the message? } }, null); 

这是执行此代码时发生的情况:

  1. 为委托分配(简单地)新线程。
  2. 该线程被赋予委托del和匿名委托( iar => ... )。
  3. 线程执行del
  4. 当它完成执行(或发生exception)时,将存储结果或exception并执行匿名委托。
  5. 在匿名委托内部,当调用EndInvoke时,将返回该方法的结果,或者抛出exception(如果发生了exception)。

请注意,上面的示例与以下示例非常不同:

 // This is pointless and is still, essentially, synchronous. del.EndInvoke(del.BeginInvoke(null, null)); 

编辑:你应该总是调用End* 。 我从来没有找到一个不调用它会出现问题的场景,但这是一个实现细节,并且依赖于未记录的行为。

最后,如果抛出exception,您的解决方案将使进程崩溃, 如果您不关心exception( del.BeginInvoke(myStruct, null, null); ,则可以简单地将null作为委托传递。 所以作为最后一个例子,您正在寻找的可能是:

 public class A { // ... void Foo(S myStruct){...} void FooAsync(S myStruct) { var del = new Action(Foo); del.BeginInvoke(myStruct, SuppressException, del); } static void SuppressException(IAsyncResult ar) { try { ((Action)ar.AsyncState).EndInvoke(ar); } catch { // TODO: Log } } } 

我会说你最好的选择是使用ThreadPool

 void bar() { ThreadPool.QueueUserWorkItem(o=> { S myStruct = new S(); test.foo(myStruct); }); } 

这会将片段排队以在单独的线程中执行。 现在您还需要注意其他事项:如果您有多个线程访问A的同一实例并且该实例修改了变量,那么您必须确保对变量进行适当的同步。

 public class A { private double sum; private volatile bool running; private readonly object sync; public A() { sum = 0.0; running = true; sync = new object(); } public void foo(S myStruct) { // You need to synchronize the whole block because you can get a race // condition (ie running can be set to false after you've checked // the flag and then you would be adding the sum when you're not // supposed to be). lock(sync) { if(running) { sum+=myStruct.Value; } } } public void stop() { // you don't need to synchronize here since the flag is volatile running = false; } } 

您可以使用回调模型解释@ 什么是AsyncCallback?

这样你的EndInvoke就不会在bar()中,而是在一个单独的回调方法中。

在示例中,EndRead(对应于EndInvoke是在称为CompleteRead的回调方法中,而不是与bar对应的调用方法TestCallbackAPM)

这是一个选项:

 ThreadPool.QueueUserWorkItem(bcl => { var bcList = (List)bcl; IAsyncResult iftAR = this.dataGridView1.BeginInvoke((MethodInvoker)delegate { int x = this.dataGridView1.Rows[0].Cells.Count - 1; for (int i = 0; i < this.dataGridView1.Rows.Count - 1; i++) { try { string imgPath = bcList[i].GifPath; Image bmpImage = Image.FromFile(imgPath); this.dataGridView1.Rows[i].Cells[x].Value =bmpImage; } catch (Exception) { continue; } } }); while (!iftAR.IsCompleted) { /* wait this*/ } }, barcodeList);