必威体育Betway必威体育官网
当前位置:首页 > IT技术

线程(Thread)与委托(Invoke和BeginInvoke)和封装

时间:2019-09-27 19:43:25来源:IT技术作者:seo实验室小编阅读:59次「手机版」
 

begininvoke

线程(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<stringbool> 逻辑 = 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() {/*包含不适合于"雷姆达表达式的"代码块*/}));

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 = "做!";

   }

   }







   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

相关阅读

java反射机制详解 及 Method.invoke解释 getMethod

ff313976的专栏 目录视图 摘要视图 订阅 赠书 | 异步2周年,技术图书免费选    每周荐书:分布式、深度学习算法、iOS(评论送书)

java反射之Method的invoke方法实现

在框架中经常会会用到method.invoke()方法,用来执行某个的对象的目标方法。以前写代码用到反射时,总是获取先获取Method,然后传入对

C# 使用反射调用方法_MethodInfo.Invoke方法

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; usi

普通方法调用,Invoke,begininvoke三者的区别总结及异步

(1)普通方法调用(直接调用)与Invoke()方法调用方法 使用的线程Id是一样的 即属于同步。(2)BeginInvoke(<输入和输出变量>,AsyncCallback ca

method.invoke()和invoke()简单理解

首先说下作用,method.invoke(Object obj,Object args[])的作用就是调用method类代表的方法,其中obj是对象名,args是传入method方法的

分享到:

栏目导航

推荐阅读

热门阅读