Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.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# 解决WinForms中的跨线程异常_C#_Winforms_Multithreading - Fatal编程技术网

C# 解决WinForms中的跨线程异常

C# 解决WinForms中的跨线程异常,c#,winforms,multithreading,C#,Winforms,Multithreading,目前我正在使用WinForms(在C#中),我必须在后台运行应用程序。为此,我使用异步。当我运行应用程序时,它会显示一个异常,如 跨线程操作无效:从创建控件“”的线程以外的线程访问该控件“” 如何解决此错误?对控件进行方法调用时,如果调用方所在的线程与创建控件的线程不同,则需要使用调用。下面是一个代码示例: // you can define a delegate with the signature you want public delegate void UpdateControlsDel

目前我正在使用WinForms(在C#中),我必须在后台运行应用程序。为此,我使用异步。当我运行应用程序时,它会显示一个异常,如

跨线程操作无效:从创建控件“”的线程以外的线程访问该控件“”


如何解决此错误?

对控件进行方法调用时,如果调用方所在的线程与创建控件的线程不同,则需要使用调用。下面是一个代码示例:

// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();

public void SomeMethod()
{
    //this method is executed by the background worker
    InvokeUpdateControls();
}

public void InvokeUpdateControls()
{
    if (this.InvokeRequired)
    {
        this.Invoke(new UpdateControlsDelegate(UpdateControls));
    }
    else
    {
        UpdateControls();
    }
}

private void UpdateControls()
{
    // update your controls here
}

希望有帮助。

通常,使用WinForms执行此类操作的最佳方法是使用,它将在后台线程上运行您的工作,但为您提供一种很好的干净方法,将状态报告回UI


在许多日常的.NET编程中,显式创建线程或调用.Invoke是一种迹象,表明您没有充分利用框架(当然,也有很多正当的理由做低级的事情,只是人们有时意识到它们不太常见).

您需要检查您试图更新的控件是否需要调用。大概是这样的:

Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;

void SetControlText(Control toSet, string text) {
  if (this.InvokeRequired) {
    this.Invoke(setterCallback, toSet, text);
  }
  else {
    setterCallback(toSet, text);
  }
}
Action setterCallback=(toSet,text)=>toSet.text=text;
void SetControlText(控件设置,字符串文本){
if(this.invokererequired){
调用(setterCallback、toSet、text);
}
否则{
setterCallback(设置,文本);
}
}

您可能会发现一种有用的模式是在与GUI交互的函数顶部进行检查,以查看您是否在正确的线程上运行,并在需要时让函数调用自身。像这样:

    public delegate void InvocationDelegate();

    public void DoGuiStuff(){
      if (someControl.InvokeRequired){
        someControl.Invoke(InvocationDelegate(DoGuiStuff));
        return;  
      }

      //GUI manipulation here
    }

使用此模式-如果调用方法时处于正确的线程上,它不会调用自身,但如果处于不同的线程上,它将调用自身,然后返回(因此GUI操作逻辑无论哪种方式都只调用一次)。

从调用更新为开始调用

// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();

public void SomeMethod()
{
    //this method is executed by the background worker
    InvokeUpdateControls();
}

public void InvokeUpdateControls()
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new UpdateControlsDelegate(UpdateControls));
    }
    else
    {
        UpdateControls();
    }
}

private void UpdateControls()
{
    // update your controls here
}

UI更改可以通过Control.Invoke()方法完成,这个跨线程异常可以使用下面的代码片段解决

void UpdateWorker()
{
   //Here ddUser is the user control
   //Action to be performed should be called within { } as like below code
   if (this.ddUser.InvokeRequired)
      ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); }));
}

我知道这个主题已经有10年历史了,但我想通过lambda选择器改进泛型的解决方案,而不是定义每种类型的setter

    private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
    {
        if (this.InvokeRequired)
            this.Invoke(MyUtils.GetSetter(selector), control, value);
        else
            DataCrawlerUtils.GetSetter(selector)(control, value);
    }  

感谢您的回复我正在我的应用程序中使用此(上面的链接)背景代码。但这显示了错误…@Victor-您必须使用.Invoke(参见其他人的示例)或使用BackgroundWorker.ReportProgress方法(以及它引发的ProgressChanged事件)访问表单。如果我使用BackgroundWorker,我总是喜欢ReportProgress而不是直接调用调用。Hi的可能重复Hi的可能重复感谢您的响应一旦我尝试了您提供的代码,实际上我调用了thred in button_click事件..但它总是转到其他部分..不进入此If(this.invokererequired){this.Invoke(new PerformSomeActionDelegate(MyAction));}@Victor:这意味着此时不需要调用。确保线程中执行的所有代码都执行“安全”(Invoke)调用以更新窗体控件。@Victor:恐怕这不可能Hi Pankaj allready我已经尝试过这段代码,但当它为真时,InvokerRequired始终为假…我在我的按钮单击事件中调用InvokerRequired…将其替换为buttonid并再次检查。我编写了这样的代码:private void btnsiteranks\u click\u 1(对象发送方,EventArgs e){InvokeUpdateControls();}public void InvokeUpdateControls(){if(this.InvokeRequired){this.BeginInvoke(new PerformSomeActionDelegate(Geturls));}else{Geturls();}}好了,现在工作正常了,只是我修改了代码,比如(!this.invokererequired)…但是我想在数据处理时显示标签文本如何…请帮助我
    public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
    {
        if (control.InvokeRequired)
            control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
        else
            DataCrawlerUtils.GetSetter(selector)(control, value);
    }
    public static Action<T, TProperty> GetSetter<T, TProperty>(
       Expression<Func<T, TProperty>> pExpression
    )
    {
        var parameter1 = Expression.Parameter(typeof(T));
        var parameter2 = Expression.Parameter(typeof(TProperty));

        // turning an expression body into a PropertyInfo is common enough
        // that it's a good idea to extract this to a reusable method
        var member = (MemberExpression)pExpression.Body;
        var propertyInfo = (PropertyInfo)member.Member;

        // use the PropertyInfo to make a property expression
        // for the first parameter (the object)
        var property = Expression.Property(parameter1, propertyInfo);

        // assignment expression that assigns the second parameter (value) to the property
        var assignment = Expression.Assign(property, parameter2);

        // then just build the lambda, which takes 2 parameters, and has the assignment
        // expression for its body
        var setter = Expression.Lambda<Action<T, TProperty>>(
           assignment,
           parameter1,
           parameter2
        );

        return setter.Compile();
    }
        SetControlSafety(txtStatus, x => x.Text, "Loading resources...");