Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/310.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_Winforms_Invoke - Fatal编程技术网

C# 跨线程操作无效:从创建控件的线程以外的线程访问控件

C# 跨线程操作无效:从创建控件的线程以外的线程访问控件,c#,multithreading,winforms,invoke,C#,Multithreading,Winforms,Invoke,我有一个设想。(Windows窗体、C#、.NET) 有一个主窗体承载一些用户控件 用户控件执行一些繁重的数据操作,因此,如果我直接调用UserControl\u Load方法,用户界面在Load方法执行期间将变得无响应 为了克服这个问题,我在不同的线程上加载数据(尽可能少地修改现有代码) 我使用了一个后台工作线程,它将加载数据,完成后将通知应用程序它已经完成了工作 现在出现了一个真正的问题。所有UI(主窗体及其子用户控件)都是在主线程上创建的。在usercontrol的LOAD方法中,我根据u

我有一个设想。(Windows窗体、C#、.NET)

  • 有一个主窗体承载一些用户控件
  • 用户控件执行一些繁重的数据操作,因此,如果我直接调用
    UserControl\u Load
    方法,用户界面在Load方法执行期间将变得无响应
  • 为了克服这个问题,我在不同的线程上加载数据(尽可能少地修改现有代码)
  • 我使用了一个后台工作线程,它将加载数据,完成后将通知应用程序它已经完成了工作
  • 现在出现了一个真正的问题。所有UI(主窗体及其子用户控件)都是在主线程上创建的。在usercontrol的LOAD方法中,我根据usercontrol上某些控件(如textbox)的值获取数据 伪代码如下所示:

    代码1

    UserContrl1_LoadDataMethod()
    {
        if (textbox1.text == "MyName") // This gives exception
        {
            //Load data corresponding to "MyName".
            //Populate a globale variable List<string> which will be binded to grid at some later stage.
        }
    }
    
    UserContrl1\u LoadDataMethod()
    {
    if(textbox1.text==“MyName”)//这会产生异常
    {
    //加载与“MyName”对应的数据。
    //填充一个globale变量列表,该列表将在稍后的某个阶段绑定到网格。
    }
    }
    
    它给出的例外是

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

    为了了解更多信息,我在谷歌上搜索了一下,并提出了一个建议,比如使用以下代码

    代码2

    UserContrl1_LoadDataMethod()
    {
        if (InvokeRequired) // Line #1
        {
            this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
            return;
        }
    
        if (textbox1.text == "MyName") // Now it wont give an exception
        {
        //Load data correspondin to "MyName"
            //Populate a globale variable List<string> which will be binded to grid at some later stage
        }
    }
    
    UserContrl1\u LoadDataMethod()
    {
    if(invokererequired)//第1行
    {
    Invoke(newmethodinvoker(UserContrl1_LoadDataMethod));
    返回;
    }
    if(textbox1.text==“MyName”)//现在它不会给出异常
    {
    //加载与“MyName”对应的数据
    //填充globale变量列表,该列表将在稍后的某个阶段绑定到网格
    }
    }
    
    但是但是。。。看来我又回到原点了。再次申请 变得没有反应。这似乎是由于执行了第#1行if条件。加载任务再次由父线程完成,而不是由我生成的第三个线程完成

    我不知道我是否认为这是对的还是错的。我是穿线新手

    如何解决此问题,以及执行第#1行if块的效果如何

    情况是这样的:我希望根据控件的值将数据加载到全局变量中。我不想更改子线程中控件的值。我永远不会从一个孩子的角度来做这件事


    因此,只有访问该值,才能从数据库中获取相应的数据。

    NET中的控件通常不是线程安全的。这意味着您不应该从它所在的线程之外的线程访问控件。为了解决这个问题,您需要调用控件,这是您的第二个示例正在尝试的


    但是,在本例中,您所做的只是将长时间运行的方法传递回主线程。当然,这不是你真正想做的。您需要重新考虑一下这一点,以便在主线程上执行的所有操作都是在此处或此处设置quick属性。

    您只想使用
    Invoke
    BeginInvoke
    来完成更改UI所需的最低工作量。您的“重”方法应该在另一个线程上执行(例如,通过
    BackgroundWorker
    ),然后使用
    控件。调用
    /
    控件。开始调用
    只是为了更新UI。这样你的UI线程就可以自由处理UI事件等

    请看我的文章,尽管这篇文章是在
    BackgroundWorker
    到达现场之前写的,但我恐怕还没有更新这方面的内容
    BackgroundWorker
    只是稍微简化了回调。

    根据(自删除后):

    我想我没有恰当地提出这个问题

    情况是这样的:我想根据控件的值将数据加载到全局变量中。我不想更改子线程中控件的值。我永远不会从一个孩子的角度来做这件事

    因此,只有访问该值,才能从数据库中获取相应的数据

    然后,您需要的解决方案应该如下所示:

    UserContrl1_LOadDataMethod()
    {
        string name = "";
        if(textbox1.InvokeRequired)
        {
            textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
        }
        if(name == "MyName")
        {
            // do whatever
        }
    }
    
    在尝试切换回控件的线程之前,请在单独的线程中进行认真的处理。例如:

    UserContrl1_LOadDataMethod()
    {
        if(textbox1.text=="MyName") //<<======Now it wont give exception**
        {
            //Load data correspondin to "MyName"
            //Populate a globale variable List<string> which will be
            //bound to grid at some later stage
            if(InvokeRequired)
            {
                // after we've done all the processing, 
                this.Invoke(new MethodInvoker(delegate {
                    // load the control with the appropriate data
                }));
                return;
            }
        }
    }
    
    UserContrl1\u LOadDataMethod()
    {
    
    如果(textbox1.text==“MyName”)/您需要查看Backgroundworker示例:

    特别是它与UI层的交互方式。根据您的帖子,这似乎回答了您的问题。

    我在
    FileSystemWatcher中遇到了这个问题,并发现以下代码解决了这个问题:

    fsw.synchroningobject=此


    控件然后使用当前表单对象来处理事件,因此将处于同一线程上。

    对于UI跨线程问题,最干净(也是最合适)的解决方案是使用SynchronizationContext,请参阅文章,它对其进行了很好的解释。

    如果您正在处理的对象没有

    (InvokeRequired)
    
    如果您使用的是类中的主窗体,而不是主窗体,并且对象位于主窗体中,但不需要invokererequired,则这非常有用

    delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);
    
    private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
    {
        MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
    }
    
    public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
    {
        objectWithoutInvoke.Text = text;
    }
    

    它的工作原理与上面相同,但如果您没有需要invokerequired的对象,但确实可以访问MainForm,则这是一种不同的方法。在xamarin studio之外的visual studio winforms原型项目中编程iOS Phone monotouch应用程序控制器时,我发现有必要这样做。更喜欢在VS中编程,而不是在xamarin st中编程我希望控制器尽可能地与手机框架完全解耦。这样,在其他框架(如Android和Windows phone)中实现这一点对于未来的使用来说会容易得多

    我想要一个解决方案,GUI可以用
    public partial class Form1 : Form
    {
        private ExampleController.MyController controller;
    
        public Form1()
        {          
            InitializeComponent();
            controller = new ExampleController.MyController((ISynchronizeInvoke) this);
            controller.Finished += controller_Finished;
        }
    
        void controller_Finished(string returnValue)
        {
            label1.Text = returnValue; 
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            controller.SubmitTask("Do It");
        }
    }
    
    public delegate void FinishedTasksHandler(string returnValue);
    
    public class MyController
    {
        private ISynchronizeInvoke _syn; 
        public MyController(ISynchronizeInvoke syn) {  _syn = syn; } 
        public event FinishedTasksHandler Finished; 
    
        public void SubmitTask(string someValue)
        {
            System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
        }
    
        private void submitTask(string someValue)
        {
            someValue = someValue + " " + DateTime.Now.ToString();
            System.Threading.Thread.Sleep(5000);
    //Finished(someValue); This causes cross threading error if called like this.
    
            if (Finished != null)
            {
                if (_syn.InvokeRequired)
                {
                    _syn.Invoke(Finished, new object[] { someValue });
                }
                else
                {
                    Finished(someValue);
                }
            }
        }
    }
    
    /// <summary>
    /// Helper method to determin if invoke required, if so will rerun method on correct thread.
    /// if not do nothing.
    /// </summary>
    /// <param name="c">Control that might require invoking</param>
    /// <param name="a">action to preform on control thread if so.</param>
    /// <returns>true if invoke required</returns>
    public bool ControlInvokeRequired(Control c, Action a)
    {
        if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
        {
            a();
        }));
        else return false;
    
        return true;
    }
    
    // usage on textbox
    public void UpdateTextBox1(String text)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
        textBox1.Text = ellapsed;
    }
    
    //Or any control
    public void UpdateControl(Color c, String s)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
        myControl.Text = s;
        myControl.BackColor = c;
    }
    
    public static class Extensions
    {
        public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) 
            where TControlType : Control
            {
                if (control.InvokeRequired)
                    control.Invoke(new Action(() => del(control)));
                else
                    del(control);
        }
    }
    
    textbox1.Invoke(t => t.Text = "A");
    
    using System.Threading.Tasks;
    using System.Threading;
    
    namespace TESTE
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Action<string> DelegateTeste_ModifyText = THREAD_MOD;
                Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
            }
    
            private void THREAD_MOD(string teste)
            {
                textBox1.Text = teste;
            }
        }
    }
    
    /// <summary>
    /// A new way to use Tasks for Asynchronous calls
    /// </summary>
    public class Example
    {
        /// <summary>
        /// No more delegates, background workers etc. just one line of code as shown below
        /// Note it is dependent on the XTask class shown next.
        /// </summary>
        public async void ExampleMethod()
        {
            //Still on GUI/Original Thread here
            //Do your updates before the next line of code
            await XTask.RunAsync(() =>
            {
                //Running an asynchronous task here
                //Cannot update GUI Thread here, but can do lots of work
            });
            //Can update GUI/Original thread on this line
        }
    }
    
    /// <summary>
    /// A class containing extension methods for the Task class 
    /// Put this file in folder named Extensions
    /// Use prefix of X for the class it Extends
    /// </summary>
    public static class XTask
    {
        /// <summary>
        /// RunAsync is an extension method that encapsulates the Task.Run using a callback
        /// </summary>
        /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
        /// <returns></returns>
        public async static Task RunAsync(Action Code)
        {
            await Task.Run(() =>
            {
                Code();
            });
            return;
        }
    }
    
        /// <summary>
        /// Run Async
        /// </summary>
        /// <typeparam name="T">The type to return</typeparam>
        /// <param name="Code">The callback to the code</param>
        /// <param name="Error">The handled and logged exception if one occurs</param>
        /// <returns>The type expected as a competed task</returns>
    
        public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
        {
           var done =  await Task<T>.Run(() =>
            {
                T result = default(T);
                try
                {
                   result = Code("Code Here");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Unhandled Exception: " + ex.Message);
                    Console.WriteLine(ex.StackTrace);
                    Error(ex);
                }
                return result;
    
            });
            return done;
        }
        public async void HowToUse()
        {
           //We now inject the type we want the async routine to return!
           var result =  await RunAsync<bool>((code) => {
               //write code here, all exceptions are logged via the wrapped try catch.
               //return what is needed
               return someBoolValue;
           }, 
           error => {
    
              //exceptions are already handled but are sent back here for further processing
           });
            if (result)
            {
                //we can now process the result because the code above awaited for the completion before
                //moving to this statement
            }
        }
    
    public  class data_holder_for_controls
    {
        //it will hold value for your label
        public  string status = string.Empty;
    }
    
    class Demo
    {
        public static  data_holder_for_controls d1 = new data_holder_for_controls();
        static void Main(string[] args)
        {
            ThreadStart ts = new ThreadStart(perform_logic);
            Thread t1 = new Thread(ts);
            t1.Start();
            t1.Join();
            //your_label.Text=d1.status; --- can access it from any thread 
        }
    
        public static void perform_logic()
        {
            //put some code here in this function
            for (int i = 0; i < 10; i++)
            {
                //statements here
            }
            //set result in status variable
            d1.status = "Task done";
        }
    }
    
    CheckForIllegalCrossThreadCalls = false
    
    Invoke(new Action(() =>
                    {
                        label1.Text = "WooHoo!!!";
                    }));
    
    this.Invoke(new MethodInvoker(delegate
                {
                    //your code here;
                }));
    
    Control.InvokeRequired Property 
    
    SynchronizationContext Post Method
    
    this.Invoke((MethodInvoker)delegate
                {
                    YourControl.Property= value; // runs thread safe
                });
    
    public static class FormExts
    {
        public static void LoadOnUI(this Form frm, Action action)
        {
            if (frm.InvokeRequired) frm.Invoke(action);
            else action.Invoke();
        }
    }
    
    private void OnAnyEvent(object sender, EventArgs args)
    {
        this.LoadOnUI(() =>
        {
            label1.Text = "";
            button1.Text = "";
        });
    }