任运自在:线程(Thread)与委托(Invoke和BeginInvoke)和封装

时间:2022-08-27 18:38:21

线程(Thread)与委托(Invoke和BeginInvoke)
这几天专门玩线程与委托,到处查找资料看,渐渐明白了用法、写法和一些注意事项;

描述:
什么是进程呢?当一个程序开始运行时,它就是一个进程,进程所指包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
Control的Invoke和BeginInvoke的参数为delegate,委托的方法是在Control的线程上执行的,也就是我们平时所说的UI线程。

何时采用简单归纳:
1、提高CPU的利用率,从而提高了程序的效率;
2、当程序运行会卡住软件界面,为了使人不用焦虑等待时,采取线程与委托来处理,从而使软件界面运行流畅;
3、处理大量数据时间较长(显示一个进度条给界面)或不需要马上得到结果反馈给软件界面时;

注意事项:线程是各自独立进行管理的,一个线程不能包含另一个线程;即:用Thread创建的线程是一个线程,Control是一个线程,这是两个独立的线程;委托则专属于Control线程;至少目前我是这样理解的,因此,初涉这个领域时不小心就会犯线程相互交涉而发生错误;更具体查看微软或网络上相关资料就不赘述了。

参考网址:
http://hi.baidu.com/jok607/blog/item/393746e72f513125b8382002.html


http://www.cnblogs.com/mashang/archive/2009/08/01/1536730.html

举例是最生动的说明,看下面例子:

要执行的操作是计算一个文本控件中有多少个字符,包含多少回车数量:

private delegate void 数字委托(int 数字);

private void 计算字数(int 回车)

{

  显示控件.Text = "字数:" + 文本.TextLength.ToString() + ";";

  显示控件.Text += "其中包含" + 回车.ToString() + "回车数";

}

//请注意上面方法包含Control线程,而下面方法不包含Control线程,并注释掉公共类传递参数,可自己调试。
private void 计算回车数量(object 数据)

{

  int 回车 = 0;
            for (int i = 0, 数量 = 数据.ToString().Length; i < 数量; i++)
            {
                if (数据.ToString().Contains("\n")) 回车++;
                if (数据.ToString().IndexOf("\n") + 1 < 数据.ToString().Length)
                    数据 = 数据.ToString().Substring(数据.ToString().IndexOf("\n") + 1);
                else break;
                if (!数据.ToString().Contains("\n")) break;
            }
           this.BeginInvoke(new 数字委托(计算字数), 回车);
}

private void 按钮_Click(object sender, EventArgs e)

{
  new Thread(new ParameterizedThreadStart(计算回车数量)).Start(文本控件.Text);
}
//声明并运行创建的线程,同时传递参数,Start传递的是object类型
运行正常。

在private void 计算回车数量(object 数据)方法中用委托来返回计算结果,参数为delegate,在最上一行声明,这样就返回到Control线程。
假如不用委托而直接用:计算字数(回车);将提示错误,原因就是不同线程发生交涉。如下:
private void 计算回车数量(object 数据)

{

  int 回车 = 0;
            for (int i = 0, 数量 = 数据.ToString().Length; i < 数量; i++)
            {
                if (数据.ToString().Contains("\n")) 回车++;
                if (数据.ToString().IndexOf("\n") + 1 < 数据.ToString().Length)
                    数据 = 数据.ToString().Substring(数据.ToString().IndexOf("\n") + 1);
                else break;
                if (!数据.ToString().Contains("\n")) break;
            }     计算字数(回车);
}

声明并运行线程语句不同写法:
--------------------------------------------------用公共类传递
public class 共类 { public string 数据 { get; set; } public int 数值 { get; set; } }
共类 文本 = new 共类(); 文本.数据 = 文本控件.Text;
Thread 计算 = new Thread(new ParameterizedThreadStart(计算回车数量)); 计算.Start(文本); 计算.Abort();
-----------------------------------------------------------------------------------------------------
string 文本 = 文本控件.Text; new Thread(delegate() { 计算回车数量(文本); }).Start();

Thread 线程 = new Thread(delegate() { 线程另存文件(); });
线程.SetApartmentState(System.Threading.ApartmentState.STA);//.MST
线程.Start();
------------------------------------------------------------------------------------

初涉的人为如何返回线程结果苦恼,采用很多种方法,我这里采用委托直接返回线程结果;

下面看看委托:一般我是这样写委托就可以了,this.BeginInvoke(new 数字委托(计算字数), 回车);

/*还看到下面的一种写法是在委托结束后回调结果的:
//此处开始异步执行,并且可以给出一个回调函数
计算.BeginInvoke(文本控件.Text, new AsyncCallback(委托回调), null);

delegate int 申明委托签名(string 传入值);

申明委托签名 计算 = new 申明委托签名(委托执行);//把委托和具体的方法关联起来


public static int 委托执行(string 文本)//委托调用的方法
{

  int 回车 = 0;

 for (int i = 0, 数量 = 数据.ToString().Length; i < 数量; i++)
            {
                if (数据.ToString().Contains("\n")) 回车++;
                if (数据.ToString().IndexOf("\n") + 1 < 数据.ToString().Length)
                    数据 = 数据.ToString().Substring(数据.ToString().IndexOf("\n") + 1);
                else break;
                if (!数据.ToString().Contains("\n")) break;
            }     return 回车;

}


public void 委托回调(IAsyncResult 返回值)

{
   this.BeginInvoke(new 数字委托(计算字数), 计算.EndInvoke(返回值));
}
*/

同样我采用委托返回结果,如果直接用:计算字数(计算.EndInvoke(返回值));将提示错误。

原因参考网址:
http://technet.microsoft.com/zh-cn/library/system.asynccallback(zh-tw).aspx

使用 AsyncCallback 委托在一个单独的线程中处理异步操作的结果。AsyncCallback 委托表示在异步操作完成时调用的回调方法。回调方法采用 IAsyncResult 参数,该参数随后可用来获取异步操作的结果。

委托注意事项:委托的方法传入参数必须对应,否则发生错误;
如:委托方法的传入参数是string则声明也必须是:private delegate void 文本委托(string 内容);

委托参数的数量必须与委托方法参数数量相等且类型必须一致;
如:委托方法的传入参数是:DateTime 日期, DateTime 预测日期, string 内容,则声明也必须对应:
private delegate void 委托(DateTime 日期, DateTime 预测日期, string 内容);

这里顺便提及是因为看到有些提问是否可以带几个参数;还有线程如何传参的,有人回复设一个公共变量来传参,提问人觉得很遗憾,各人方法不尽相同,也属正常,无可非议。

同时还应该注意:线程与异步委托完成时间是不定的,设计时也应该慎重考虑或用调试决定。

以上就是这些天专门玩线程与委托的一些经验,今天凭着思路就写了这些,知道写得不好,看了莫笑。

初学灵活变通和试验调试相对比较弱,这里再给一个直接调用多参数方法例子:
Thread 线程 = new Thread(delegate() { this.Invoke(new Action(() => 加载快捷菜单(快捷菜单, 快捷参数, 快捷事件))); });
线程.Start();

其实线程没那么难搞定,这里给个定式:
Thread 线程 = new Thread(delegate()
 {
     this.Invoke(new Action(() => {/*如果涉及UI线程原代码放这里,如果没有删除这句*/}));
 });
线程.Start();

new Thread(delegate() { this.Invoke(new Action(delegate() { 乾坤大挪移(快捷菜单, 乾坤大挪移参数); })); }).Start();

使用匿名委托:
this.Invoke(new Action(delegate() { /*任何语句或方法*/}));
new Thread(delegate() { /*不涉及UI线程任何语句或方法*/ }).Start();

Thread 线程 = new Thread(delegate()
{/*原代码放这里就可以了*/}); 线程.Start();线程.Join();/*后续其他代码*/

有时方法外使用线程可以改为方法内使用线程(多参数传递)是一样的,下面是改动的例子:
private void 乾坤大挪移(ContextMenuStrip 菜单名, string[] 子参数)
 {
      Thread 线程 = new Thread(delegate()
     {
        this.Invoke(new Action(() =>
        {
           /*原代码放这里就可以了*/
        }));
     }); 线程.Start();
 }

其实线程和委托使用起来是很方便的,特别是跨线程访问也很简单,只要是涉及到控件线程就使用委托就可以了,下面是改动的例子:
private void 时间_Tick(object sender, EventArgs e)
 {
      Thread 线程 = new Thread(delegate()
      {
          if (秒 < 59) 秒++; else { 秒 = 0; 分++; } if (分 == 60) { 分 = 0; 时++; } if (时 == 5) 时 = 0;
          this.Invoke(new Action(() =>
          显示时间.Text = DateTime.Parse(时.ToString("0:") + 分.ToString("00:") + 秒.ToString("00")).ToLongTimeString()));
       }); 线程.Start();
 }
当使用异步委托(BeginInvoke)需注意,有可能造成界面反应更忙,一般不与界面反应有关不轻易使用。

        private void button1_Click(object sender, EventArgs e)
        {

            IAsyncResult 调用返回 = listBox1.BeginInvoke(new Action(() =>
            {
                Thread.Sleep(2000);
                listBox1.Items.Add(Thread.CurrentThread.Name);
            }));
            listBox1.EndInvoke(调用返回);

            listBox1.Invoke((EventHandler)delegate
            {
                Thread.Sleep(2000);
                listBox1.Items.Add(Thread.CurrentThread.Name);//运行到这里,其实把上一个函数的睡着的异步给弄醒了
            });
            listBox1.BeginInvoke(new MethodInvoker(delegate
            {
                Thread.Sleep(2000);//调用的时候需要等待的时间,异步是指CPU自己有空闲的时候合理分配空间给予执行;跟此时间无关;
                listBox1.Items.Add(Thread.CurrentThread.Name);
            }));

        }补充参考

Task 类

var t = Task.Factory.StartNew(() => button1_Click(null ,null ));

线程池

            System.Threading.ThreadPool.QueueUserWorkItem(
                new System.Threading.WaitCallback(一些长期任务));

            System.Threading.ThreadPool.QueueUserWorkItem(
                new System.Threading.WaitCallback(另一个长期任务),传递);

        private void 一些长期任务(Object state)
        {
            // 插入代码来执行一项艰巨的任务。

            this.Invoke(new Action(() => { resultLabel.Text = "0"; }));

            int aa = 100;
            do
            {
                System.Threading.Thread.Sleep(1000);
                this.Invoke(new Action(() => { resultLabel.Text = (int.Parse(resultLabel.Text) + 1).ToString(); }));
            } while (--aa > 0);
        }

        private void 另一个长期任务(Object 参数)
        {
            // 插入代码来执行一项艰巨的任务,参数包装

           string aa=参数.ToString();
        }

使用组件BackgroundWorker 类

-----------------------------------------------------------------------------------

 有关封装与三目运算符应用:

Func< string bool > 逻辑 =  delegate ( string 年信息 )
{
     if  (年信息.Contains( "11" ))  return  false ;
     return  true ; /*在这里可以写多语句处理,写在调用之前*/ 
};
return  (年信息.Contains( "00" )) ?  true  : 逻辑(年信息);
上面利用有参有返回(仅1个参数和返回值)
委托有参数无返回:
            Action<DateTime[]> 日期计算 = delegate(DateTime[] 日期)
            {
                /*在这写处理代码*/
            };
调用:日期计算(new DateTime[] { 日期1, 日期2 });
委托无参有返回:
            Func<string[]> 处理 = delegate
            {
                string[] 内容 = new string[0];
                /*在这写处理代码*/
                return 内容;
            };
Action 显示 =  delegate ()
{
    升起提示窗体(显示事件.Text.Replace( "\r\n" "" )); 
};
Action 显示 = ()=>
{
};
Action<int> 显示 = (参数)=>
{
};
显示(/*在需要调用的地方写这行代码*/);

点击打开链接<多线程描述>

并行处理:using System.Threading.Tasks;/*并行运算*/            Parallel.Invoke(() =>{/*代码块*/}); 

Parallel.Invoke(new System.Action(delegate() {/*包含不适合于"雷姆达表达式的"代码块*/}));

任运自在:线程(Thread)与委托(Invoke和BeginInvoke)和封装

Parallel.Invoke(delegate()
            {
                this.BeginInvoke(new Action(delegate()
                { /*适用嵌套循环提高速度*/}));
             });

 下面转来自微软代码例子:

            Action<object> action = (object obj) =>
            {
                Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
            };
            Task t1 = new Task(action, "alpha");
            Task t2 = Task.Factory.StartNew(action, "beta");
            t2.Wait();
            t1.Start();

            Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId);
            t1.Wait();
            Task t3 = new Task(action, "gamma");
            t3.RunSynchronously();
            t3.Wait();


        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;
        }
        private void startAsyncButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy != true)
            {
                // 启动异步操作。
                backgroundWorker1.RunWorkerAsync();
            }
        }
        private void cancelAsyncButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.WorkerSupportsCancellation == true)
            {
                // 取消异步操作。
                backgroundWorker1.CancelAsync();
            }
        }
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 1; i <= 10; i++)
            {
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // 执行耗时的操作,并报告进度。
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress(i * 10);
                }
            }
        }
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
        }
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                resultLabel.Text = "取消!";
            }
            else if (e.Error != null)
            {
                resultLabel.Text = "错误: " + e.Error.Message;
            }
            else
            {
                resultLabel.Text = "做!";
            }
        }

任运自在:线程(Thread)与委托(Invoke和BeginInvoke)和封装

    Public Sub 定时事件(ByVal state As Object)
        Me.BeginInvoke(
            New Action(
                       Sub()
                           移动字幕.Left = 移动字幕.Left - 1
                           If 移动字幕.Right < 0 Then 移动字幕.Left = Me.Width
                       End Sub)
                   )
    End Sub