当前位置:编程学习 > C#/ASP.NET >>

「玩一玩」InvokeHelper,让跨线程访问/修改主界面控件不再麻烦

--------------------编程问答-------------------- 野比君
睡了没?


--------------------编程问答--------------------
引用 1 楼  的回复:
野比君
睡了没?
你还不睡啊 --------------------编程问答-------------------- 这位兄台,好象你的代码很复杂。我前几天写了一下,表述力不佳,仅贴代码。

    public class ThreadRunCode
    {
        public Thread th;
        public bool bExceptRun=true;//是否结束线程,这个可以在外控制线程
        public bool bSleep=true;//本次线程的时间片是否执行事件。
        private System.Windows.Forms.Form frm;

        public delegate void ThreadRunMthod();
        public event ThreadRunMthod threadRunMthod;

        public void RunThread() 
        {
            th = new Thread(new ThreadStart(RunThreadCode));
            th.Start();
        }
        private void RunThreadCode() 
        {
            do
            {
                if (bSleep) 
                {
                    continue;
                }
                this.frm.Invoke(threadRunMthod);
            } while (bExceptRun);
        }
    }

在Form中调用举例:

     public int i;
     //others code
     ThreadRunCode threadRunCode=new ThreadRunCode(this);
     threadRunCode.threadRunMthod += new ThreadRunMthod(ThreadRunCode_threadRunMthod);
     threadRunCode.bSleep=false;
     threadRunCode.RunThread();
    //others code
      void ThreadRunCode_threadRunMthod()
      {
            textBox1.text=(i++).ToString();
      }

    这样写是不是也可以实现多线程,其实我在想,多线程同一时刻尽量少,应该他存在的时候才启动。比如你说的例子,我认为在要修改界面的时候再建一个线珵,修改完后就终止线程,再改变的时候再建一个。
      --------------------编程问答-------------------- 这个有点意思,平时用的蛮多,从来没想到去封装成类 --------------------编程问答--------------------
引用 3 楼  的回复:
这位兄台,好象你的代码很复杂。我前几天写了一下,表述力不佳,仅贴代码。
C# code

    public class ThreadRunCode
    {
        public Thread th;
        public bool bExceptRun=true;//是否结束线程,这个可以在外控制线程
        public bool bSleep=true;/……
思路上不一样。我写的这个是「在多线程时修改控件」,而不是「为了改控件而多线程」。欢迎你照你的思路完善下去,然后争取做到最简化。 --------------------编程问答-------------------- 远离反射,通常更能够保证性能。而且以下方法更加直观和方便。

在你的工程中的扩展方法类中写下一个SafeCall方法:
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public static class Extensions
    {
        public static void SafeCall(this Control ctrl, Action callback)
        {
            if (ctrl.InvokeRequired)
                ctrl.Invoke(callback);
            else
                callback();
        }
    }
}
它只是把你要保护起来的代码作为一个回调而已。然后任何需要保护一些代码的地方都可以这样调用:
using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(h =>
            {
                for (var i = 0; i < 10000000; i++)
                {
                    label1.SafeCall(() =>
                    {
                        label1.Text = i.ToString();
                    });
                    Thread.Sleep(100);
                }
            });
        }

    }
}
--------------------编程问答-------------------- c#是很优雅的语言。通常你都不需要使用沉重和弯弯绕的“反射”技巧来,而可以更加轻松地封装你的方法。 --------------------编程问答-------------------- 当然,使用lamda是我的一个“坏毛病”。其实这里完全可以使用传统的匿名委托写法:
using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(h =>
            {
                for (var i = 0; i < 10000000; i++)
                {
                    label1.SafeCall(delegate()
                    {
                        label1.Text = i.ToString();
                    });
                    Thread.Sleep(100);
                }
            });
        }

    }
}
--------------------编程问答-------------------- 注明一下:此文内容基于.NET 2.0
新的.NET有很方便的实现跨线程访问的能力,参见楼上 --------------------编程问答-------------------- 太好了,正纠结于这个问题 --------------------编程问答-------------------- 多线程交给系统去处理吧。 --------------------编程问答--------------------
引用 8 楼  的回复:
当然,使用lamda是我的一个“坏毛病”。其实这里完全可以使用传统的匿名委托写法:C# code
using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
 ……

不错,作为分享,应该使用较新的技术。
C#的出路不在于跟java比谁更老。 --------------------编程问答--------------------
引用 12 楼  的回复:
引用 8 楼  的回复:

当然,使用lamda是我的一个“坏毛病”。其实这里完全可以使用传统的匿名委托写法:C# code
using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Fo……
不会新技术,没招。。相信用2.0的还是不少的。。 --------------------编程问答-------------------- 支持一个 --------------------编程问答--------------------
引用 8 楼  的回复:
当然,使用lamda是我的一个“坏毛病”。其实这里完全可以使用传统的匿名委托写法:

。。。。

连P哥也习惯于复制粘贴了。。。 --------------------编程问答-------------------- 标记,收藏~
看看有好用的方法,以后再用上。之前用过的是类似与#3的方法。 --------------------编程问答--------------------
学习来着
--------------------编程问答-------------------- 其实我是来灌水的 --------------------编程问答-------------------- --------------------编程问答-------------------- 正好最近用多线程,学习了。 --------------------编程问答--------------------
 
public delegate void DoWorkD(int N);

        public DoWorkD d;

        public void DoWork(int Nums)
        {
            if (textBox1.InvokeRequired)
            {
                textBox1.Invoke(d, Nums);
            }
            else
                textBox1.Text += Nums.ToString() + "-";
        }



        public void ThreadDoWork()
        {
            Thread.Sleep(new Random().Next(100, 1000));
            int i=0;
            while (true)
            {
                d = new DoWorkD(DoWork);
                Thread.Sleep(new Random().Next(100, 1000));
                DoWork(i);
                i++;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 20; i++)
            {
                Thread a = new Thread(ThreadDoWork);
                a.IsBackground = true;
                a.Start();
            }
        }

这样就可以了.net 2.0

例子在这http://download.csdn.net/detail/wawd74520/4479927 --------------------编程问答-------------------- 学习来着 哈哈 --------------------编程问答-------------------- --------------------编程问答--------------------
引用 21 楼  的回复:
C# code
 
public delegate void DoWorkD(int N);

        public DoWorkD d;

        public void DoWork(int Nums)
        {
            if (textBox1.InvokeRequired)
            {
                textB……


其实楼主的做法,就是想省略掉声明一大堆的委托 --------------------编程问答-------------------- 唉。还得从效率上考虑啊。
尤其是多线程 --------------------编程问答-------------------- 实在不行,其实可以把句柄传过去。怎么改都可以。- - 虽然很烂 --------------------编程问答-------------------- 乔巴过来学习下 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 只用一行代码的飘过


--------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 多线程学好了还真管用! --------------------编程问答-------------------- 支持一个,我也是用.net 2.0的。现在有这个InvokeHelper,应该会方便很多了。
野比,加一个:BeginInvoke 下去会更完美一点。

以前破旧的代码:

   Private Delegate Sub ShowMsgCallBack(ByVal msg As String)  
   Private Delegate Sub SetBtnTextCallBack(ByVal text As String)  

   Private Sub ShowPanel()  
        If Me.PnlDownInfo.InvokeRequired Then  
            Dim mi As New MethodInvoker(AddressOf ShowPanel)  
            Me.BeginInvoke(mi)  
        Else  
            Me.PnlDownInfo.Visible = Not Me.PnlDownInfo.Visible  
        End If  
    End Sub  

 Private Sub ShowMsg(ByVal msg As String)  
        If Me.LBDownloadInfo.InvokeRequired Then  
            Dim cb As New ShowMsgCallBack(AddressOf ShowMsg)  
            '如使用Invoke会等到函数调用结束,而BeginInvoke不会等待直接往后走   
            Me.BeginInvoke(cb, New Object() {msg})  
        Else  
            Me.LBDownloadInfo.Text = msg  
            Me.LBDownloadInfo.Refresh()  
        End If  
    End Sub  

 Private Sub PerFinishDownLoad(ByVal fname As String)  
        If Me.LvFiles.InvokeRequired Then  
            Dim cb As New ShowMsgCallBack(AddressOf PerFinishDownLoad)  
            Me.BeginInvoke(cb, New Object() {fname})  
        Else  
            Dim item As ListViewItem  
            item = Me.LvFiles.FindItemWithText(fname)  
            If item IsNot Nothing Then  
                item.SubItems(2).Text = "完成"  
            End If  
        End If  
    End Sub  
  
    Private Sub ResetDownLoadPrg()  
        If Me.InvokeRequired Then  
            Dim mi As New MethodInvoker(AddressOf ResetDownLoadPrg)  
            Me.BeginInvoke(mi)  
        Else  
            Me.SmoothProgressBar1.Value = 0  
            Me.LBDownloadInfo.Text = "下载信息"  
        End If  
    End Sub  
  
    Private Sub ResetDownLoadBtn()  
        If Me.BtnRunUpdate.InvokeRequired Or Me.BtnCancel.InvokeRequired Then  
            Dim mi As New MethodInvoker(AddressOf ResetDownLoadBtn)  
            Me.BeginInvoke(mi)  
        Else  
            Me.BtnRunUpdate.Enabled = True  
            Me.BtnRunUpdate.Text = "完成(&F)"  
            Me.BtnCancel.Enabled = False  
        End If  
    End Sub  
--------------------编程问答-------------------- 这是一个值得学习的东西,不过这知识实现一个功能,其实还可以网上面加东西 --------------------编程问答-------------------- 昨天加班 木上Q就躺了 --------------------编程问答-------------------- Invoke(new MethodInvoke(new Action(deleagte()
{

})));

子线程中需要访问 控件的话,我现在习惯了这样,很少判断这InvokeRequired个属性了 --------------------编程问答-------------------- 我没看错????
循环创建了20个线程?访问同一个控件?没加锁?
引用 21 楼  的回复:
C# code
 
public delegate void DoWorkD(int N);

        public DoWorkD d;

        public void DoWork(int Nums)
        {
            if (textBox1.InvokeRequired)
            {
                textB……
--------------------编程问答-------------------- 还是SP大哥技术牛,这时候想到了用扩展方法......

向你学习,另外这个获取异步委托的返回值,怎么做啊,这个异步委托,很少用,不太熟悉.

引用 6 楼  的回复:
远离反射,通常更能够保证性能。而且以下方法更加直观和方便。

在你的工程中的扩展方法类中写下一个SafeCall方法:C# code
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public static class Extensions
    {
        ……
--------------------编程问答-------------------- 虽然看不到,但是先Marki下。 --------------------编程问答-------------------- --------------------编程问答-------------------- 好像很水的内容 --------------------编程问答-------------------- --------------------编程问答-------------------- 不错,感谢楼主分享 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 看来要多学习了 --------------------编程问答-------------------- 探讨的问题越来越精彩了,不错 --------------------编程问答-------------------- 哇!不懂! --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 从来还没用过多线程,看了这篇帖子,现在可以试试水了 --------------------编程问答-------------------- 好帖先收藏一哈  嘿嘿 --------------------编程问答-------------------- 的确我也感觉线程与UI的通讯很麻烦,至少我觉得写出来的代码一点都不优雅,感觉很难给第二个人看得一目了然,我也很苦恼这个事情,不知各位高手有什么高招 --------------------编程问答-------------------- LZ还应该反treeView,ListView等操作控件内子属性的也增加上。这样就完美了。偶的就是这样做的。 --------------------编程问答--------------------
引用 57 楼  的回复:
LZ还应该反treeView,ListView等操作控件内子属性的也增加上。这样就完美了。偶的就是这样做的。

只是个例子。我不打算给出什么「完美」的东西,那样会剥夺阅读的人进步的机会。对于不想思考只想伸手的人,我不愿意给他们提供任何帮助。 --------------------编程问答-------------------- --------------------编程问答-------------------- mark,最近正在学着用多线程~~ --------------------编程问答-------------------- 楼主用了反射吗?貌似很耗资源的说

我有个思路,楼主或各位大大给点意见

就是重写FORM类,写一个专门支持跨线程操作的ThreadForm,继承于Form

在这个类中,
public delegate void ThreadInvokeDelegate(string type,params object[] args);
#region 静态方法,供其他线程调用,用于访问Form
public static event ThreadInvokeDelegate OnThreadInvoke;
public static ThreadInvoke(string type,params object[] args)
{
   if(OnThreadInvoke!=null)
      OnThreadInvoke(type,args);
}
#endregion

在真正的form中(比如Form1),构造函数里注册这个静态事件
Form1.OnThreadInvoke+=new ThreadInvokeDelegate(Form1_OnThreadInvoke);

这样就把任意线程调用Form1.ThreadInvoke方法转到了Form1_OnThreadInvoke中处理
这里的string type作为标识,来区分Form1需要进行的不同的操作,比如刷新列表,还是写日志,还是其他什么操作

以上想法本人技术有限,不能很好的实现,希望有大哥能完善,并分享一下 --------------------编程问答-------------------- 不错.......... --------------------编程问答-------------------- .Net 4.5 的async和await异步编程,貌似可以完全无视那些begin和end开头的异步方法了。。 --------------------编程问答-------------------- --------------------编程问答-------------------- 看了一下,没看懂 --------------------编程问答-------------------- --------------------编程问答-------------------- 厉害!! --------------------编程问答-------------------- 很厉害 --------------------编程问答-------------------- 用.NET 3.5的飘过 --------------------编程问答-------------------- 学习到了,但是还是想问,多个线程同时使用一个控件,不会死锁吗 --------------------编程问答-------------------- 那就加锁呗
引用 70 楼  的回复:
学习到了,但是还是想问,多个线程同时使用一个控件,不会死锁吗
--------------------编程问答-------------------- 积分怎么弄啊!! --------------------编程问答-------------------- 积分怎么弄啊!! --------------------编程问答-------------------- 线程间的访问默认是不允许的,但是这个开关是可以打开的,开关打开,虽然可以跨线程访问,但会带来安全问题。

还有,在.net 4.0中,微软已经了为我们提供了ui线程和非UI线程之间的访问办法,

可以参照control.invoke,control.beginInvoke方法 --------------------编程问答-------------------- --------------------编程问答-------------------- 尽快回答是到就会受到asdad --------------------编程问答-------------------- this.Control.CheckForIllegalCrossThreadCalls = false;
夸线程访问控件,这一句代码不是就完了么。。 --------------------编程问答--------------------
引用 77 楼  的回复:
this.Control.CheckForIllegalCrossThreadCalls = false;
夸线程访问控件,这一句代码不是就完了么。。


就用这么一行代码的路过~~~


--------------------编程问答--------------------
引用 77 楼  的回复:
this.Control.CheckForIllegalCrossThreadCalls = false;
夸线程访问控件,这一句代码不是就完了么。。
是的,就完了。 --------------------编程问答-------------------- 恩,不错,值得一看! --------------------编程问答--------------------
引用 70 楼  的回复:
学习到了,但是还是想问,多个线程同时使用一个控件,不会死锁吗


你运行了这里的程序了吗?它死锁了吗?

自己动手测试才是硬道理。 --------------------编程问答-------------------- --------------------编程问答-------------------- 没好好看  没环境测试啊  悲催的  装了n次系统了 --------------------编程问答-------------------- 非常感谢!
学习了! --------------------编程问答-------------------- --------------------编程问答-------------------- 内容不错 收藏了 --------------------编程问答-------------------- 各位大神,我最近在使WPF主窗口中弹出一个新窗口,但老是出错,好像也涉及到多线程,但这一块实在是不怎么懂,求各位指导一下……
http://topic.csdn.net/u/20120810/14/17b442ae-5f57-4781-80a2-75aebb5568c6.html?seed=275715377&r=79381756#r_79381756
--------------------编程问答--------------------
引用 40 楼  的回复:
我没看错????
循环创建了20个线程?访问同一个控件?没加锁?
引用 21 楼  的回复:

C# code

public delegate void DoWorkD(int N);

public DoWorkD d;

public void DoWork(int Nums)
{
if (textBox1.InvokeRequired)
{
textB……
……


通过endinvoke,回调 --------------------编程问答-------------------- 上面的例了应该不能算多线程。。多线程对同一控件或变量访问是要加锁的 --------------------编程问答-------------------- 用得着这么麻烦吗?用Timer控件不行吗? --------------------编程问答-------------------- 虽然挺简单到 但是还是好东西  赞一个 --------------------编程问答-------------------- 看过野比大哥几遍文章,对于我这个新人来说,受益不少,谢谢。! --------------------编程问答-------------------- 很好很强大  值得学习啊
--------------------编程问答-------------------- 很好很强大  值得学习啊
--------------------编程问答--------------------
引用 44 楼  的回复:
好像很水的内容

同意,虽然谁都写得出来,但觉得写出来没任何意义,所以谁都不高兴写。
实际是否要委托本身就看需求,多线程是为了提高效率而存在的,不是为了降低效率,到处用委托违背了多线程的存在意义。
而实际对于简单的输出显示,应该直接对其操作,不要回调,设置CheckForIllegalCrossThreadCalls为false即可,有时候不检查能大大提高效率,而即使检测也不代表就一定能正常,因为多线程操作共享资源一般需要加锁,这和是否回调操作无关。 --------------------编程问答-------------------- 讲的好啊。。。 --------------------编程问答-------------------- 学习下。楼主 总是很给力 --------------------编程问答-------------------- 都没用过Invoke. --------------------编程问答-------------------- --------------------编程问答-------------------- 不怎么用反射,没用过回调。。。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,