Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.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# 调用函数的最佳方式是什么?_C#_Multithreading_Delegates_Invoke - Fatal编程技术网

C# 调用函数的最佳方式是什么?

C# 调用函数的最佳方式是什么?,c#,multithreading,delegates,invoke,C#,Multithreading,Delegates,Invoke,我的程序有两个线程在运行,线程1执行一些操作来控制线程2上运行的表单中的标签。因此,我必须使用委托并调用Form1类中的函数来访问标签。下面是我的代码,它工作得很好。然而,我想知道是否有一个更短,更好的方法来做到这一点 delegate void Change_Status_Call_Back(string status_changed); public void change_status(string status_changed) { if (this.la

我的程序有两个线程在运行,线程1执行一些操作来控制线程2上运行的表单中的标签。因此,我必须使用委托并调用Form1类中的函数来访问标签。下面是我的代码,它工作得很好。然而,我想知道是否有一个更短,更好的方法来做到这一点

delegate void Change_Status_Call_Back(string status_changed);
    public void change_status(string status_changed)
    {
        if (this.label_status.InvokeRequired)
        {
            Change_Status_Call_Back obj = new Change_Status_Call_Back(change_status);
            this.Invoke(obj, new object[] { status_changed });
        }
        else
        {
            this.label_status.Text = status_changed;
        }
    }
我会这样做:

public void change_status(string status_changed)
{
    this.label_status.InvokeSafely(c => c.Text = status_changed);
}
您需要此扩展方法:

    public static void InvokeSafely(this Control control, Action<Control> action)
    {
        if (control.InvokeRequired)
        {
            control.Invoke((Action)(() => action?.Invoke(control)));
        }
        else
        {
            action?.Invoke(control);
        }
    }
publicstaticvoid安全调用(此控件、操作)
{
if(control.invokererequired)
{
control.Invoke((操作)(()=>Action?.Invoke(控制));
}
其他的
{
动作?调用(控制);
}
}
这个问题“主要基于观点”。尽管如此,你还是触动了我的愤怒,所以

您应该跳过
invokererequired
检查:

public void change_status(string status_changed)
{
    this.Invoke((MethodInvoker)(() => this.label_status.Text = status_changed));
}
无论如何,框架都必须有效地检查
invokererequired
,因为它需要支持在UI线程上进行调用而不会出现死锁。所以签入代码是多余的。在这样的UI代码中,总是在委托调用中包装方法体的开销是无关紧要的,特别是因为如果您正在编写此代码,那么当
invokererequired
无论如何都是真的时,该方法可能不会被称为异常(即,无论如何都不会采用“快速路径”)

更好的方法是使用更现代的机制来处理跨线程访问,例如
async
/
await
Progress
类。然后,您根本不必编写对
Invoke()
的显式调用


不久前,我在这里更深入地咆哮道:

环顾四周,我想到了这个:

// UPDATE DISPLAY items (using Invoke in case running on BW thread).
IAsyncResult h = BeginInvoke((MethodInvoker)delegate
{
    FooButton.Text = temp1;
    BarUpdown.Value = temp2;
}
);
EndInvoke(h);  // Wait for invoke to complete.
h.AsyncWaitHandle.Close();  // Explicitly close the wait handle.
                            // (Keeps handle count from growing until GC.)
详情:

  • 我完全删除了
    if(invokererequired)
    。(从Peter Duniho的回答中发现)Invoke()在UI线程上运行良好。在只在UI线程上运行的代码中,UI操作不需要特殊处理。在仅在非UI线程上运行的代码中,将所有UI操作包装在Invoke()中。在可以在UI线程或非UI线程上运行的代码中,同样地,将所有UI操作包装在Invoke()中。在UI线程上运行时,总是使用Invoke()会增加一些开销,但是:开销不大(我希望如此);无论如何,这些操作在UI线程上运行的频率较低;而且通过始终使用Invoke,您不必对UI操作编写两次代码。我被出卖了
  • 我将
    Invoke()
    替换为
    BeginInvoke()。。EndInvoke()。。AsyncWaitHandle.Close()
    。(已找到。)
    Invoke()
    可能只是
    BeginInvoke()。。EndInvoke()
    ,所以这只是内联扩展(稍微多一些目标代码;稍微快一些执行)。添加
    AsyncWaitHandle.Close()
    可以解决其他问题:在非UI线程上运行时,
    Invoke()
    会留下数百个句柄,直到垃圾回收为止。(看着任务管理器中的句柄数增长是很可怕的。)使用
    BeginInvoke()。。EndInvoke()
    使延迟句柄保持不变。(令人惊讶的是:仅使用
    BeginInvoke()
    不会留下句柄;看起来是
    EndInvoke()
    造成的。)使用
    AsyncWaitHandle.Close()
    显式杀死死句柄消除了残留句柄的[表面]问题。在UI线程上运行时,
    BeginInvoke()。。EndInvoke()
    (比如
    Invoke()
    )没有留下任何句柄,因此
    AsyncWaitHandle.Close()
    是不必要的,但我认为它也不昂贵
  • IsDisposed测试在比赛条件下似乎是安全的,但我认为它没有必要。我担心BackgroundWorker会调用()操作;当表单处于挂起状态时,单击可以触发UI线程上的回调,从而关闭()表单,然后消息循环执行此操作。(不确定会发生这种情况。)
问题:(我将在这里进行更新,当某些东西工作时。)我将所有UI更新从在UI计时器kludge上运行更改为使用Invoke()(如上所述),现在关闭表单在大约20%的时间内在竞争条件下失败。如果用户单击停止我的后台工作程序,则单击“关闭”后即可正常工作。但是,如果用户直接单击close,则会在关闭窗体的UI线程上触发回调;触发另一个标记后台工作进程停止的事件;后台工作程序继续运行,并在EndInvoke()崩溃,原因是“无法访问已处理的对象。对象名称:'MainWin'。位于System.Windows.Forms.Control.MarshaledInvoke(控件调用方,委托方法,对象[]参数,布尔同步)…”。在
EndInvoke()周围添加
if(!this.IsDisposed){}
。。AsyncWaitHandle.Close()
无法修复它

选项:返回到使用窗体计时器:使BW将其更改写入十几个全局“邮箱”变量。让计时器执行
FooButton.Text=nextfoobuttonext,等等。大多数这样的赋值几乎不起任何作用,因为设置表单字段只会在值实际更改时更新显示。(为了清晰和减少复制对象,请将邮箱变量初始化为null,并让计时器执行
if(nextfootbuttonext!=null){FooButton.Text=nextfootbuttonext;nextfootbuttonext=null;}
,等等)计时器每隔这么多毫秒在UI消息循环上放置一个新事件,这比调用()更愚蠢)s在计时器回调上更新显示会将每次更新延迟[高达]计时器间隔。(恶心。)

工作选项:仅使用
BeginInvoke()
。为什么让BW等待每个调用完成?1) temp1和temp2似乎是作为引用传递的-如果它们在
BeginInvoke()
之后被更改,则新值将获胜。(但这并不坏。)2)temp1和temp2可能超出范围。(但是在最后一个引用消失之前它们不会被释放吗?)3)等待确保BW一次只有一个被调用的操作挂起-