Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 没有EndInvoke的异步调用?_C#_Asynchronous_Delegates_Action - Fatal编程技术网

C# 没有EndInvoke的异步调用?

C# 没有EndInvoke的异步调用?,c#,asynchronous,delegates,action,C#,Asynchronous,Delegates,Action,以下面的类为例 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')。ba

以下面的类为例

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')。bar方法需要尽快返回。关于委托、BeginInvoke、EndInvoke、线程池等的文档并不能帮助我找到解决方案

这是一个有效的解决方案吗

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

我想说,您最好的选择是使用
线程池

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 (i.e. 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;
    }
}

您可以使用解释过的回调模型@

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


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

您不需要调用
EndInvoke
;不叫它仅仅意味着:

  • 您无法从该方法获取返回值
  • 在方法执行期间抛出的任何异常都将消失
听起来你想“开火然后忘记”,所以最简单的方法是使用匿名委托,例如:

var del = new Action(foo.Bar);
del.BeginInvoke(iar =>
{
   try
   {
      del.EndInvoke(iar);
   }
   catch (Exception ex)
   {
      // Log the message?
   }
}, null);
执行此代码时会发生以下情况:

  • 为委托分配(简单地说)一个新线程
  • 线程被赋予委托
    del
    和匿名委托(
    iar=>…
  • 线程执行
    del
  • 当完成执行(或发生异常)时,将存储结果或异常,并执行匿名委托
  • 在匿名委托内部,当调用
    EndInvoke
    时,返回方法的结果,或者抛出异常(如果发生异常)
  • 请注意,上述示例与以下示例非常不同:

    // This is pointless and is still, essentially, synchronous.
    del.EndInvoke(del.BeginInvoke(null, null));
    
    编辑:您应该始终调用
    End*
    。我从来没有发现过不调用它会出现问题的场景,但是这是一个实现细节,并且非常复杂

    最后,如果抛出异常,您的解决方案将使进程崩溃,如果您不关心异常,只需将null作为委托传递(
    del.BeginInvoke(myStruct,null,null);
    )。因此,作为最后一个示例,您要寻找的可能是:

    public class A
    {
        // ...
        void Foo(S myStruct){...}
        void FooAsync(S myStruct)
        {
            var del = new Action<S>(Foo);
            del.BeginInvoke(myStruct, SuppressException, del);
        }
    
        static void SuppressException(IAsyncResult ar)
        {
            try
            {
                ((Action<S>)ar.AsyncState).EndInvoke(ar);
            }
            catch
            {
                // TODO: Log
            }
        }
    }
    
    公共A类
    {
    // ...
    void Foo(S myStruct){…}
    void fooancy(S myStruct)
    {
    var del=新动作(Foo);
    del.BeginInvoke(myStruct、SuppressException、del);
    }
    静态无效抑制异常(IAsyncResult ar)
    {
    尝试
    {
    ((Action)ar.AsyncState).EndInvoke(ar);
    }
    抓住
    {
    //TODO:日志
    }
    }
    }
    
    这是一个选项:

    ThreadPool.QueueUserWorkItem(bcl =>
    {
        var bcList = (List<BarcodeColumn>)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);
    
    ThreadPool.QueueUserWorkItem(bcl=>
    {
    var bcList=(List)bcl;
    IAsyncResult iftAR=this.dataGridView1.BeginInvoke((MethodInvoker)委托
    {
    int x=this.dataGridView1.Rows[0].Cells.Count-1;
    对于(int i=0;i
    True,不“要求”调用
    EndInvoke
    ,但如果不这样做,将导致内存泄漏@马特克莱恩:不,没有。SLaks的回答有些正确,但在某些情况下,一些跟踪是通过
    Begin/End Invoke
    对来完成的-一个例子是:如果你不在
    Socket
    操作上调用
    EndInvoke
    ,你的Socket性能计数器将完全失控(没有内存泄漏,这些值将非常不正确)。也许这将是一个有价值的评论,可以添加到SLaks的答案中。它可能会帮助其他人,因为根据此线程,这是“必须调用EndInvoke()”的正确方法。如果不需要返回值,是否可以传递null而不是回调方法?我想情况就是这样