C# 委托 线程

delegate匿名方法(匿名函数)

1. 函数和方法等价。匿名方法能够让你声明一个方法体而不需要给它指定一个名字,它们以一个“普通的”方法存在,但是在你的代码中没有任何方法显式调用它。,返回参数不需要声明,会根据[语句块]决定。

2. 匿名方法只能在使用委托的时候创建,它们通过delegate关键字创建或者Lambda表达式(匿名函数)。定义方式:delegate(显式参数,显式参数) {语句块}

 delegate(bool x) { return x ? 1 : 2; }

3. 匿名函数总是和委托一齐使用 :    定义委托变量=匿名函数

 Func<bool, int> fun = delegate(bool x) { return x ? 1 : 2; };

Lambda表达式(匿名函数)

1. "Lambda表达式"可以是一个表达式也可以是一个匿名函数,Lambda表达式都使用Lambda运算符“=>”。Lambda运算符的左边是[输入参数(如果有)],右边是[表达式]或[语句块],返回参数不需要声明,会根据[表达式]或[语句块]决定。表达式只能有一条语句,语句块可以有多条语句。

(参数1,参数2)=>表达式或语句块

1. () => { return 1; };          //无参数 =>语句块
2. ( x) => x * 5;                //单参数,隐式类型 =>表达式
3. ( x) => { return x * 5; };    //单参数,隐式类型=>语句块
4. (int x) => x * 5;             //单参数,显式类型=>表达式
5. (int x) => { return x * 5; }; //单参数,显式类型=>语句块
6. ( x, y) => x * y ;            //多参数,隐式类型=> 表达式

2. Lambda表达式可以和委托一齐使用 :    定义委托变量=Lambda表达式

 Func<bool, int> fun = (bool x) => { return x ? 1 : 2; };

delegate委托

1. 委托的用途就是把方法当作参数来进行传递。

public delegate int MethodtDelegate(int x, int y);//定义委托
MethodtDelegate md = (x, y) => { return x * y; };//定义委托变量
md(1, 2);//调用委托

2. 可以用+=对一个委托变量绑定多个方法。这叫多播委托,利用 -=移除方法。

md += (x, y) => { return x + y; };
md += (x, y) => { return x - y;  };

3. C#预定义的三种泛型委托Action<>、Func<>、Predicate<>

3.1. Action<>  至少0个参数,至多16个参数,无返回值。

Action<int,string, float> action=null;

3.2. Func<>    至少1个参数(返回值参数),至多16个参数,有返回值。最后一个参数为返回值参数

Func<int,string, float> func=null;

3.3. Predicate<> 只有一个参数,默认返回bool。

Predicate一般用于Lambda表达式里面的条件,表示定义一组条件并确定指定对象是否符合这些条件的方法,此方法常在集合的查找中被用到。如:数组,正则拼配的结果集中被用到。

Predicate<int> predicate=null;

0 异步委托介绍:

Invoke 开始同步调用委托(同步调用Invoke,无论是否新开线程都会导致阻塞Invoke所在的线程)
BeginInvoke 开始异步调用委托
EndInvoke 返回异步调用的结果集(当使用BeginInvoke异步调用方法时,如果方法未执行完,EndInvoke方法就会一直阻塞,直到被调用的方法执行完毕)
AsyncCallback 结束异步调用后要执行的操作。

1. (Control的Invoke和BeginInvoke)是在Control线程上调用。所以如果在Control线程上调用Control的Invoke或BeginInvoke来进行调用某个委托方法来达到跨线程或异步是错误的。那Control的Invoke和BeginInvoke的用途是什么呢,他的用途是让其他线程的某个方法在Control所在线程上执行。如果在其他线程上调用Control的Invoke,这时候其他线程会被阻塞,直到Control线程执行完其他线程才会在继续执行。而Control的BeginInvoke不会让其他线程阻塞。

2. (Delegate的Invoke和BeginInvoke)是在线程池中调用。 所以一般如果某个方法执行时间特别长,都会用Delegate的BeginInvoke执行,这样Control线程就不会被阻塞。但要注意,如果在Control线程中调用Delegate的Invoke,虽然Delegate的Invoke是从线程池的线程同步调用,但他还是会阻塞Control线程的,所以要用Delegate的BeginInvoke才能实现异步。还有一种情况就是在线程池上用Delegate的Invoke会导致线程池上Delegate所在线程被阻塞,直到Control所在线程上执行完线程池上Delegate所在线程才能继续执行。

3. 使用BeginInvoke时特别要注意的就是BeginInvoke里面所用到的变量必须全部使用参数形式传进去,防止异步时被幕改变量值。

1 异步有阻塞:

例如有一个程序现在要分别调用 接口1和接口2,并要获取到他们两个接口的结果集后才能继续执行后面的代码。接口1调用耗时为10秒,接口2调用耗时5秒,如果是同步调用这两个接口并要处理这两个接口返回值时,一共需要耗时15秒。但如果我们使用异步同时调用这两个接口,那我们一般最多只需要10秒就可以返回两个接口的结果集。但是由于我们要在调用接口的这个方法退出前必须要获取到接口返回的结果,所以我们要用EndInvoke;这时候调用线程会处于阻塞状态。

//定义调用接口1委托
Func<string, string> getInfo1 = (string txt) => { /*调用接口1*/ Thread.Sleep(10000); return "1"; };
//定义调用接口2委托
Func<string, string> getInfo2 = (string txt) => { /*调用接口2*/ Thread.Sleep(5000); return "2"; };

//开始调用接口1
IAsyncResult ar1 = getInfo1.BeginInvoke("调用接口1", null, null);
//开始调用接口2
IAsyncResult ar2 = getInfo2.BeginInvoke("调用接口2", null, null);

string result1 = getInfo1.EndInvoke(ar1);//处理接口1返回结果
string result2 = getInfo2.EndInvoke(ar2);//处理接口2返回结果

for (int i = 0; i < 10; i++)
{

}

2 异步有阻塞加超时:

利用WaitOne方法,这个方法的超时设置指代码运行但这句代码时开始计算时间,所以同时有多个BeginInvoke异步调用时要注意。

//定义调用接口1委托
Func<string, string> getInfo1 = (string txt) => { /*调用接口1*/ Thread.Sleep(1000); return "1"; };
//定义调用接口2委托
Func<string, string> getInfo2 = (string txt) => { /*调用接口2*/ Thread.Sleep(7000); return "2"; };

//开始调用接口1
IAsyncResult ar1 = getInfo1.BeginInvoke("调用接口1", null, null);
//开始调用接口2
IAsyncResult ar2 = getInfo2.BeginInvoke("调用接口2", null, null);

//string result1 = getInfo1.EndInvoke(ar1);//处理接口1返回结果
//string result2 = getInfo2.EndInvoke(ar2);//处理接口2返回结果

int TimeOut = 5000;//超时时间为5秒
Stopwatch sw = new Stopwatch();
sw.Start();

if (ar1.AsyncWaitHandle.WaitOne(TimeOut))
{
getInfo1.EndInvoke(ar1); //方法成功执行返回值
sw.Stop();
TimeOut -= (int)sw.ElapsedMilliseconds;//减掉第一个异步的耗时
}
else
{
sw.Stop();
TimeOut = 0;
//超时
}

if (ar2.AsyncWaitHandle.WaitOne(TimeOut))
{
getInfo2.EndInvoke(ar2); //方法成功执行返回值
}
else
{
//超时
}

for (int i = 0; i < 10; i++)
{

}

3 异步无阻塞单向:

例如异步写日志,Control线程把要写入的日志信息传递给另外一个线程,不管另外一个线程的执行进度,Control线程继续执行其他工作。

//定义写日志委托
Action<string> writeLog = (string log) => { /*写日志内容*/ };
//在写日志委托回调函数李自动调用EndInvoke
AsyncCallback callFun = (result) => { ((Action<string>)result.AsyncState).EndInvoke(result); };
//开始异步写日志
writeLog.BeginInvoke("日志内容", callFun, writeLog);

4 异步无阻塞加回调:

例如有一个UI程序按钮现在要分别调用接口1和接口2,并要获取到他们两个接口的结果集后要更新按钮上的文字。接口调用耗时为10秒,接口2调用耗时5秒,如果是同步调用这两个接口并要处理这两个接口返回值时,一共需要耗时15秒。UI画面会卡死15秒。但如果我们使用上面1的方法,那UI界面也至少要卡死10秒。所以如果我们把EndInvoke放在一个AsyncCallback里面处理,当我们按下按钮时,会异步调用这两个接口,由于UI线程没有被阻塞,所以界面不会卡死,当等到接口被调用完成后将会自动调用AsyncCallback利用EndInvoke返回结果处理按钮上的文字。

private void asynbtn_Click(object sender, EventArgs e)
{
//定义调用接口1委托
Func<string, string> getInterface1 = (string txt) => { Thread.Sleep(5000); return "1"; };
//定义调用接口2委托
Func<string, string> getInterface2 = (string txt) => { Thread.Sleep(7000); return "2"; };

//异步全局信息
AsyncGlobalInfo agi = new AsyncGlobalInfo();
agi.gi1 = getInterface1;
agi.gi2 = getInterface2;

//开始调用接口1
IAsyncResult ar1 = getInterface1.BeginInvoke("调用接口1参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result1 = ic.gi1.EndInvoke(result); ic.isCompleted1 = true; setText(ic); }), agi);
//开始调用接口2
IAsyncResult ar2 = getInterface2.BeginInvoke("调用接口2参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result2 = ic.gi2.EndInvoke(result); ic.isCompleted2 = true; setText(ic); }), agi);
}
//定义处理按钮委托
public void setText(AsyncGlobalInfo infoall)
{
//判断所有的异步操作都完成
if (infoall.isCompleted1 && infoall.isCompleted2)
{
this.Invoke(new Action(() => { this.button1.Text = infoall.result1 + " " + infoall.result2; }));
}
}

/// <summary>
/// 异步全局信息
/// </summary>
public class AsyncGlobalInfo
{
public Func<string, string> gi1;
public bool isCompleted1 = false;
public string result1 = null;
public Func<string, string> gi2;
public bool isCompleted2 = false;
public string result2 = null;
}

5 异步无阻塞加回调加凭据:

以上4的方法都有一个问题,那就是耗时问题。1如果这个异步调用必须在指定时间内完成,但4的方法都没有时间限制,所以有可能造成永久等待。2例如一个按钮点击了两次,如果第一次处理时间比第二次长,有可能第二次结果会被第一次覆盖。那要怎么做呢。一般添加一个凭据来验证异步调用和回调是同一次。

private string textBoxToken = "";//textBox凭据

private void asynbtn_Click(object sender, EventArgs e)
{
//定义调用接口1委托
Func<string, string> getInterface1 = (string txt) => { Thread.Sleep(5000); return "1"; };
//定义调用接口2委托
Func<string, string> getInterface2 = (string txt) => { Thread.Sleep(5000); return "2"; };

textBoxToken = "asynbtn";
//异步全局信息
AsyncGlobalInfo agi = new AsyncGlobalInfo();
agi.gi1 = getInterface1;
agi.gi2 = getInterface2;
agi.token = textBoxToken;

//开始调用接口1
IAsyncResult ar1 = getInterface1.BeginInvoke("调用接口1参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result1 = ic.gi1.EndInvoke(result); ic.isCompleted1 = true; if (this.textBoxToken == ic.token) setText(ic); }), agi);
//开始调用接口2
IAsyncResult ar2 = getInterface2.BeginInvoke("调用接口2参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result2 = ic.gi2.EndInvoke(result); ic.isCompleted2 = true; if (this.textBoxToken == ic.token) setText(ic); }), agi);
}

private void copybtn_Click(object sender, EventArgs e)
{
//定义调用接口1委托
Func<string, string> getInterface1 = (string txt) => { Thread.Sleep(3000); return "3"; };
//定义调用接口2委托
Func<string, string> getInterface2 = (string txt) => { Thread.Sleep(3000); return "4"; };

textBoxToken = "copybtn";
//异步全局信息
AsyncGlobalInfo agi = new AsyncGlobalInfo();
agi.gi1 = getInterface1;
agi.gi2 = getInterface2;
agi.token = textBoxToken;

//开始调用接口1
IAsyncResult ar1 = getInterface1.BeginInvoke("调用接口1参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result1 = ic.gi1.EndInvoke(result); ic.isCompleted1 = true; if (this.textBoxToken == ic.token) setText(ic); }), agi);
//开始调用接口2
IAsyncResult ar2 = getInterface2.BeginInvoke("调用接口2参数", new AsyncCallback((result) => { AsyncGlobalInfo ic = (AsyncGlobalInfo)result.AsyncState; ic.result2 = ic.gi2.EndInvoke(result); ic.isCompleted2 = true; if (this.textBoxToken == ic.token) setText(ic); }), agi);
}

//定义处理按钮委托
public void setText(AsyncGlobalInfo infoall)
{
//判断所有的异步操作都完成
if (infoall.isCompleted1 && infoall.isCompleted2)
{
this.Invoke(new Action(() => { this.textBox1.Text = infoall.result1 + " " + infoall.result2; }));
}
}

/// <summary>
/// 异步全局信息
/// </summary>
public class AsyncGlobalInfo
{
public string token;
public Func<string, string> gi1;
public bool isCompleted1 = false;
public string result1 = null;
public Func<string, string> gi2;
public bool isCompleted2 = false;
public string result2 = null;
}

上面为什么阻塞的用来超时处理,无阻塞用凭据处理呢,你可以理解阻塞的是按代码顺序执行的,无阻塞类似于并发。

原文地址:https://www.cnblogs.com/tlmbem/p/10832853.html

时间: 2024-08-07 12:49:19

C# 委托 线程的相关文章

[2014-9-15]异步委托线程高级

昨天悲剧,帮别人调代码,愣没调出来.还没时间写博文了. 忧桑... 昨天的今天写吧,今天也没学,就上了一天的课. 1 首先窗口间数据进行传递.也就是父窗口与子窗口数据进行传递. 用委托实现 ①,在同一命名控件下定义委托. ②,在子窗口创建委托实例. ③,在父窗口创建子窗口对象,传递委托方法. public delegate void SetString(string str);    public partial class Form1 : Form    {        public For

委托线程三部曲(引用)

#异步委托(异步方法调用)一: 同步调用 : 程序顺序执行时-->调用很耗时的[方法]-->阻塞当前线程-->[方法]调用完成-->继续执行. 异步调用 : -------------------------------------------------------------------------------- 委托例子 : 自写委托如下 : public delegate String MyDelegate(String name); 微软会自动提供下面两个方法 : IAs

C# 委托 线程 窗体假死

转载:http://www.cnblogs.com/smartls/archive/2011/04/08/2008981.html 异步调用是CLR为开发者提供的一种重要的编程手段,它也是构建高性能.可伸缩应用程序的关键.在多核CPU越来越普及的今天,异步编程允许使用非常少的线程执行很多操作.我们通常使用异步完成许多计算型.IO型的复杂.耗时操作,去取得我们的应用程序运行所需要的一部分数据.在取得这些数据后,我们需要将它们绑定在UI中呈现.当数据量偏大时,我们会发现窗体变成了空白面板.此时如果用

使用异步委托执行线程(delegate)

由于异步委托产生的线程与应用程序主线程是分开执行的,若主线程要获取异步委托线程的结果,则主线程需要等待异步委托的执行结果. BeginInvoke是Delegate类型的一个方法,它的返回类型为IAsyncResult,通过该接口,可以获得异步委托的执行信息. 1.投票技术:等待异步委托结果的一种方法是投票技术,该技术的实现原理是通过IAsyncResult接口的IsComplete属性来检查委托是否完成了任务.EndInvote方法获取执行结果.举个例子,如下: 执行后的效果: 2.访问等待句

创建线程之异步委托

首先定义一个runtaketime委托: public delegate int runtaketime(int data,int ms); 定义一个静态的方法,参数,返回类型和定义的委托一样 static public int taketime(int data, int ms) { Console.WriteLine("time begin!"); Thread.Sleep(ms); Console.WriteLine("time end!"); return

记5.28大促压测的性能优化&mdash;线程池相关问题

目录: 1.环境介绍 2.症状 3.诊断 4.结论 5.解决 6.对比java实现 废话就不多说了,本文分享下博主在5.28大促压测期间解决的一个性能问题,觉得这个还是比较有意思的,值得总结拿出来分享下. 博主所服务的部门是作为公共业务平台,公共业务平台支持上层所有业务系统(2C.UGC.直播等).平台中核心之一的就是订单域相关服务,下单服务.查单服务.支付回调服务,当然结算页暂时还是我们负责,结算页负责承上启下进行下单.结算.跳支付中心.每次业务方进行大促期间平台都要进行一次常规压测,做到心里

线程+任务+同步

线程: 对于所有需要等待的操作,例如移动文件,数据库和网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务.一个进程的多个线程可以同时运行在不同的CPU上或多核CPU的不同内核上. 线程是程序中独立的指令流.在VS编辑器中输入代码的时候,系统会分析代码,用下划线标注遗漏的分号和其他语法错误,这就是用一个后台线程完成.Word文档需要一个线程等待用户输入,另一个线程进行后台搜索,第三个线程将写入的数据存储在临时文件中.运行在服务器上的应用程序中等待客户请求的线程成为侦听器线程.

记5.28大促压测的性能优化—线程池相关问题

目录: 1.环境介绍 2.症状 3.诊断 4.结论 5.解决 6.对比java实现 废话就不多说了,本文分享下博主在5.28大促压测期间解决的一个性能问题,觉得这个还是比较有意思的,值得总结拿出来分享下. 博主所服务的部门是作为公共业务平台,公共业务平台支持上层所有业务系统(2C.UGC.直播等).平台中核心之一的就是订单域相关服务,下单服务.查单服务.支付回调服务,当然结算页暂时还是我们负责,结算页负责承上启下进行下单.结算.跳支付中心.每次业务方进行大促期间平台都要进行一次常规压测,做到心里

C#线程、任务总结(一)

使用委托的轻型线程 异步委托 创建线程的一种简单的方式是定义一个委托,并异步调用它.委托是方法的类型安全的引用(类型安全代码指访问被授权可以访问的内存位置.例如,类型安全代码不能从其他对象的私有字段读取值.它只从定义完善的允许方式访问类型才能读取.类型安全的代码具备定义良好的数据类型).Delegate 类还支持异步地调用方法,在后台,Delegate类会创建一个执行任务的线程.(PS: 委托使用线程池来完成异步任务.)定义好委托之后,可以有不同的技术异步调用委托,并返回结果. 投票 一种技术是