精华内容
下载资源
问答
  • C#跨线程访问控件

    2013-03-17 14:28:48
    C#跨线程访问控件,用了线程池和Action委托和Control的Invoke方法
  • C# 跨线程访问控件

    千次阅读 2018-04-06 00:21:08
    因为C#安全机制的问题,不是本线程创建的控件,是不能...二、创建委托,利用C#的Invoke 或 beginInvoke 方法从创建控件的线程来执行跨线程调用; 三、利用BackgroundWorker组件 和 DoWorkEventHandler 、 RunWorker...

    因为C#安全机制的问题,不是本线程创建的控件,是不能直接访问的。

    以下提供了两种解决办法:

    一、让程序不检查跨线程调用,加入一行代码就可以了,但这样背离了C#安全机制的初衷;

    二、创建委托,利用C#的Invoke 或 beginInvoke 方法从创建控件的线程来执行跨线程调用;

    三、利用BackgroundWorker组件 和 DoWorkEventHandler 、 RunWorkerCompletedEventHandler委托将运算量大的逻辑运算和界面更新分开,减少UI线程负担。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace thread
    {
        public partial class Form1 : Form
        {
            Thread t;
            int count = 0;
            private delegate void SetTextCallback();//方法二、利用委托代理(推荐)
            public Form1()
            {
                //方法一,加入下面这句让程序不检查跨线程调用是否合法(全局变量,慎用!!)
                Control.CheckForIllegalCrossThreadCalls = false;  
                InitializeComponent();
                t = new Thread(new ThreadStart(ThreadLb));
                t.Start();
            }
    
            private void ChangeLb()
            {
                if (this.lb1.InvokeRequired)//判断是否跨线程调用
                {
                    SetTextCallback d = new SetTextCallback(ChangeLb);
                    //this.BeginInvoke(d);//异步调用代理,程序继续往下执行
                    this.Invoke(d); //同步调用委托,程序在此等待
                }
                else
                {
                    count++;
                    this.lb1.Text = count.ToString();
                }
    
            }
    
    
    
            //使用委托的Lambda表达式
            private void ChangeLb_()
            {
                this.Invoke(new Action(() => {
                    this.lb1.Text = count.ToString();
                }))
    
            }
    
            private void ThreadLb()
            {
                while(true)
                {
                    Thread.Sleep(30);
                    ChangeLb();
                }
            }
    
    
            //第3种方法
            private void button4_Click(object sender, EventArgs e)
            {
                using (BackgroundWorker bw = new BackgroundWorker())
                {
                    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
                    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                    bw.RunWorkerAsync("Tank");
                }         
            }
    
            void bw_DoWork(object sender, DoWorkEventArgs e)
            {       
                // 这里是后台线程, 用来处理复杂的逻辑
                Thread.Sleep(5000);
                e.Result = e.Argument + "工作线程完成";
            }
    
            void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                //这时后台线程已经完成,并返回了主线程,所以可以直接修改UI控件了 
                this.label4.Text = e.Result.ToString(); 
            }
    
    
        }
    }
    

     

    展开全文
  • c#跨线程访问控件

    2016-05-26 16:57:01
    C#中,在线程中(包括 BackgroundWorker 控件)访问主线程UI时会抛出异常:因为.net禁止了跨线程调用控件。下面用一个办法来进行跨线程的调用,且不阻塞控件。以给控件赋Text值为例,因为这个比较实用。 private ...

    在C#中,在线程中(包括 BackgroundWorker 控件)访问主线程UI时会抛出异常:因为.net禁止了跨线程调用控件。下面用一个办法来进行跨线程的调用,且不阻塞控件。以给控件赋Text值为例,因为这个比较实用。

    private delegate void Thread_SetControl_Text(Control c,string str);
            private void ThreadSetControlText(Control c,string str)
            {
                if (c.InvokeRequired)//等待异步 
                {
                    Thread_SetControl_Text fc = new Thread_SetControl_Text(ThreadSetControlText);
                    this.Invoke(fc, new object[2] {c, str});
    
                }
                else
                {
                    c.Text = str;
                    c.Refresh();
    
                }
            }
    在线程中如下调用(假设有 textBox1) :

    ThreadSetControlText(this.textBox1,"ABC");




    展开全文
  • c# 跨线程访问控件

    2020-09-29 20:22:50
    这样可以实现在另外的线程访问控件,但是这种方法并不安全,不能保证C#跨线程访问控件的运行时错误。 // 在初始化控件后就可以把这个属性设置为false,编译器就不会对跨线程访问做检查 public Form1() { Initialize...

    简述

    访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。所以,确保以线程安全方式访问控件是非常重要的。

     


    一、举个例子

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace treadtest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private Thread test_thread_run = null;
            private static object dataLock = new object();        // 多线程访问链表使用的锁
            private List<string> dataList = new List<string>();
            bool is_test_thread_run;
    
            private uint m_serialNumber = 0;
    
            private void TestThreadFunc()
            {
                while (is_test_thread_run)
                {
                    lock (dataLock)
                    {
                        if (0 == dataList.Count)
                        {
                            Thread.Sleep(20);
                            continue;
                        }
    
                        // 每当链表中有数据时就在listview中显示该条数据
                        int count = 0;
                        while (0 < (count = dataList.Count))
                        {
                            string param = dataList[0];
                            if (null != param)
                            {
                                ListViewItem item = new ListViewItem();
                                item.Text = (++m_serialNumber).ToString();
                                item.SubItems.Add(DateTime.Now.ToString());
                                item.SubItems.Add(param);
                                // 这里会报异常,因为在不是创建listview的线程中访问了它
                                listView.Items.Add(item);
                            }
                            dataList.Remove(param);
                        }
                    }
                }
            }
    
            private void button_start_thread_Click(object sender, EventArgs e)
            {
                // 开启线程处理链表中的数据
                is_test_thread_run = true;
                test_thread_run = new Thread(new ThreadStart(TestThreadFunc));
                test_thread_run.IsBackground = true;
                test_thread_run.Start();
    
                this.button_start_thread.Enabled = false;
            }
    
            private void button_pushback_data_Click(object sender, EventArgs e)
            {
                lock (dataLock)
                {
                    // 链表中添加一条数据
                    this.dataList.Add(this.textBox_data.Text);
                }
            }
        }
    }

    这里的这个程序想要开启一个线程,在这个线程里一直去读取一个链表中的数据,只要链表中有数据就把这个数据和当前的时间添加到上面的listview中。但是当访问listview控件显示数据的时候就会出现异常中断,提示你在不是创建控件listview的线程里访问了它。

     


    二、解决办法

    (一)、设置CheckForIllegalCrossThreadCalls为false

    第一种方法是把CheckForIllegalCrossThreadCalls设置为false,禁止编译器对跨线程访问作检查。这样可以实现在另外的线程访问控件,但是这种方法并不安全,不能保证C#跨线程访问控件的运行时错误。

    // 在初始化控件后就可以把这个属性设置为false,编译器就不会对跨线程访问做检查
    public Form1()
    {
        InitializeComponent();
        CheckForIllegalCrossThreadCalls = false;
    }

    (二)、使用MethodInvoker

    MethodInvoker 表示一个委托,该委托可以执行托管代码中声明为void且不接受任何参数的任何方法。在对控件的 invoke 方法进行调用时或需要一个简单委托又不想自己定义时可以使用该委托。我们可以这样修改一下我们上面的代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace treadtest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private Thread test_thread_run = null;
            private static object dataLock = new object();        // 多线程访问链表使用的锁
            private List<string> dataList = new List<string>();
            bool is_test_thread_run;
    
            private static object displayDataObj = new object();
            private uint m_serialNumber = 0;
    
            private void TestThreadFunc()
            {
                while (is_test_thread_run)
                {
                    lock (dataLock)
                    {
                        if (0 == dataList.Count)
                        {
                            Thread.Sleep(20);
                            continue;
                        }
    
                        // 每当链表中有数据时就在listview中显示该条数据
                        int count = 0;
                        while (0 < (count = dataList.Count))
                        {
                            string param = dataList[0];
                            if (null != param)
                            {
                                ShowTransComRecData(param);
                            }
                            dataList.Remove(param);
                        }
                    }
                }
            }
    
            private void ShowTransComRecData(string str)
            {
                lock (displayDataObj)
                {
                    // 这里使用MethodInvoker委托,在委托中访问listview控件显示数据
                    this.Invoke((MethodInvoker)delegate()
                    {
                        try
                        {
                            ListViewItem item = new ListViewItem();
                            item.Text = (++m_serialNumber).ToString();
                            item.SubItems.Add(DateTime.Now.ToString());
                            item.SubItems.Add(str);
                            listView.Items.Add(item);
                        }
                        catch
                        { }
                    });
                }
            }
    
            private void button_start_thread_Click(object sender, EventArgs e)
            {
                // 开启线程处理链表中的数据
                is_test_thread_run = true;
                test_thread_run = new Thread(new ThreadStart(TestThreadFunc));
                test_thread_run.IsBackground = true;
                test_thread_run.Start();
    
                this.button_start_thread.Enabled = false;
            }
    
            private void button_pushback_data_Click(object sender, EventArgs e)
            {
                lock (dataLock)
                {
                    // 链表中添加一条数据
                    this.dataList.Add(this.textBox_data.Text);
                }
            }
        }
    }

    (三)、利用委托

    每个控件都有一个InvokeRequired属性,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它。此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,实质上和上面一种方法是一样的,这个做法保证了控件的安全。你可以这样理解,有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了。可以这样修改最上面的代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace treadtest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private Thread test_thread_run = null;
            private static object dataLock = new object();        // 多线程访问链表使用的锁
            private List<string> dataList = new List<string>();
            bool is_test_thread_run;
    
            private static object displayDataObj = new object();
            private uint m_serialNumber = 0;
    
            private void TestThreadFunc()
            {
                while (is_test_thread_run)
                {
                    lock (dataLock)
                    {
                        if (0 == dataList.Count)
                        {
                            Thread.Sleep(20);
                            continue;
                        }
    
                        // 每当链表中有数据时就在listview中显示该条数据
                        int count = 0;
                        while (0 < (count = dataList.Count))
                        {
                            string param = dataList[0];
                            if (null != param)
                            {
                                ShowTransComRecData(param);
                            }
                            dataList.Remove(param);
                        }
                    }
                }
            }
    
            delegate void SetTextCallBack(string text);
    
            private void ShowTransComRecData(string str)
            {
                // 判断是不是创建该控件以外的线程想访问它
                if (this.listView.InvokeRequired)
                {
                    SetTextCallBack stcb = new SetTextCallBack(ShowTransComRecData);
                    this.Invoke(stcb, new object[] { str });
                }
                else
                {
                    ListViewItem item = new ListViewItem();
                    item.Text = (++m_serialNumber).ToString();
                    item.SubItems.Add(DateTime.Now.ToString());
                    item.SubItems.Add(str);
                    listView.Items.Add(item);
                }
            }
    
            private void button_start_thread_Click(object sender, EventArgs e)
            {
                // 开启线程处理链表中的数据
                is_test_thread_run = true;
                test_thread_run = new Thread(new ThreadStart(TestThreadFunc));
                test_thread_run.IsBackground = true;
                test_thread_run.Start();
    
                this.button_start_thread.Enabled = false;
            }
    
            private void button_pushback_data_Click(object sender, EventArgs e)
            {
                lock (dataLock)
                {
                    // 链表中添加一条数据
                    this.dataList.Add(this.textBox_data.Text);
                }
            }
        }
    }

    按照上面的三种方式修改后,可以看到程序能够按照我们的想的那样一直显示链表的数据了

     

    展开全文
  • C#中遇到的多线程问题,该程序为跨线程访问控件的实例
  • 通过反射技术,将异步加载的数据,安全的跨线程会显到UI界面.使用简单
  • 对于winform程序来说,当我们点击按钮,需要消耗一定时长才能拿到数据后才能显示在界面上某个控件上的情况,我们通常会专门开一个线程去拿数据,这样不会造成界面处于假死状态 2.常规做法 1 //启动线程 2 ...

    1.背景

    对于winform程序来说,当我们点击按钮,需要消耗一定时长才能拿到数据后才能显示在界面上某个控件上的情况,我们通常会专门开一个线程去拿数据,这样不会造成界面处于假死状态

    2.常规做法

     1         //启动线程
     2         private void button1_Click(object sender, EventArgs e)
     3         {
     4             Thread t = new Thread(GetValue);
     5             t.IsBackground = true;
     6             t.Start();
     7         }
     8 
     9         void GetValue() 
    10         {
    11             Thread.Sleep(2000);//耗时操作
    12           
    13             string content = "hi!";
    14             label1.Invoke(new Action(() => { label2.Text = content; }));//控件回调,显示数据
    15         }

     

    3.封装异步加载数据,形成轮子,方便以后直接使用示例

          /*演示InvokeHelper(我们即将构造的帮助类)的使用方法*/

    //启动线程 private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(GetValue); t.IsBackground = true; t.Start(); } //加载比较耗时的数据 void GetValue() { Thread.Sleep(2000); string value = "hello,异步!"; InvokeHelper.Invoke(this, "DoWork", value);//演示invoke方法 InvokeHelper.Set(label1, "Text", "");//演示set方法 object temp = InvokeHelper.Get(label1, "Tag"); //演示Get方法 InvokeHelper.Set(label2, "Text", temp); } //控件赋值赋值 public void DoWork(string value) { richTextBox1.Text = value; }

     

    4.该Rabbit.InvokeHelper.DLL使用说明手册

       方法简介:

     I.public static object Invoke(Control control, string methodName, params object[] args)  

        功能:主要用于其他线程做完耗时操作拿到数据后,给UI线程上的控件赋值

        参数:Contorl指代控件,methodName表示当天cs文件中一个方法的名字,用于在这个方法下进行控件的赋值操,args是这个methodName这个方法的参数类型

        使用方式:见上面代码的调用过程.相信你能看懂,并不难。

    II.  public static object Get(Control control, string propertyName)

        功能:从其他线程里跨线程拿取UI控件的值

        参数 :Control,将要访问的控件;propertyName,控件的属性值

        使用方式:见上面的使用案例

    III.  public static object Get(Control control, object noncontrol, string propertyName)

       功能:从其他线程里跨线程拿取UI控件的值

       参数:Control,将要访问的控件;noncontrol,一个扩展对象,如果不为null则该函数的功能时拿取该对象的属性值;propertyName,控件的属性值

       使用方式:object t=InvokeHelper.Get(label1,label1.TopLevelControl,"Text"); //这样拿到的将是lable控件顶层容器的Text属性值

    IV.  public static void Set(Control control, string propertyName, object value)

       功能:从其他线程给UI线程控件设置一个值

       参数:Control,控件;propertyName,控件属性值;value,将要赋值给控件属性的数据

       使用方式:见上面的实例

    V. public static void Set(Control control, object noncontrol, string propertyName, object value)

       功能:从其他线程给UI线程控件设置一个值

       参数:Control,控件;propertyName,控件属性值;noncontrol,一个扩展对象,如果不会null,则该函数的功能是设置该扩展对象的属性值;value,将要赋值给控件属性的数据

      使用方式: InvokeHelper.Set(label1, label1.TopLevelControl, "Text","Hello");将hello赋值给label1的父级控件的text属性

    5.dll下载

    xml注释下载  密码:hd0z        

    dll下载      密码:9wve

    6.关于

    本人qq:739462304@qq,欢迎大家一起交流学习

    转载于:https://www.cnblogs.com/huanxi/p/5703282.html

    展开全文
  • public partial class Form1 : Form { public Form1() { InitializeComponent(); //方法一:不进行跨线程安全检查 //System.Windows.Forms.Control.CheckForIlle...
  • 参考以下博客,我认为写的比较好,在此记录作为以后的参考 https://www.cnblogs.com/zhangchenliang/archive/2012/08/20/2647396.html
  • dataGridView.BeginInvoke((MethodInvoker)delegate { dataGridView.Rows[newIdx].Cells["CameraID02"].Value = Convert.ToInt32(lstRecord[0].ToString().Trim());...
  • C#不久,这是我总结的两点跨线程访问控件; 1、访问文本框,获得文本框的内容,文本框名:txtMessage  private delegate string GetSendMessageDelegate();//定义委托  private string ...
  • 但是本人在使用C#4.5的时候发现自己跨线程访问控件属性居然完全不提示错误。代码如下 public partial class Form1 : Form  {  public Form1()  {  InitializeComponent();  }  private ...
  • 从程序设计上来说,只有创建界面的主线程才能访问界面上的控件,禁止跨线程访问控件,可以使用委托代理来解决这一问题   下面例子是错误的 private void Form1_Load ( object sender , EventArgs e )   { ...
  • C# 跨线程访问UI线程控件C#中,由于使用线程和调用UI的线程属于两个不同的线程,如果在线程中直接设置UI元素的属性,此时就会出现跨线程错误。    下面介绍两种解决方案  第一种:使用控件自带的Invoke或者...
  • C#跨线程修改控件

    2021-02-20 16:53:49
    C#跨线程修改控件 委托 c#中跨线程访问修改控件会报错,此时解决方法之一就是用委托,下面代码实现了利用委托方式赋值checkbox delegate void SetBoolCallback(bool sate) private void SetBool(bool state) { if...
  • 主要介绍了C#之WinForm跨线程访问控件,实例讲述了跨线程访问控件的简单实现方法与用法,需要的朋友可以参考下
  • .net原则上禁止跨线程访问控件,因为...可以实现访问,但是出不出错不敢保证C#跨线程访问控件运行时错误。 使用MethodInvoker即可解决: 原代码: privatevoidbtnOK_Click(objectsender,EventArgse) { tslInf...
  • 主要介绍了C#跨线程访问控件问题解决方案,有需要的朋友可以参考一下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 405
精华内容 162
关键字:

c#跨线程访问控件

c# 订阅