标签:网络连接 返回值 out inter 相对 mint 中断 检索 res
管理线程开销最好的方式:
微软早就替我们想到了,为我们实现了线程池。
CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。
线程池初始化时是没有线程的,线程池里的。线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。
这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。
通过CLR线程池所建立的线程总是默认为后台线程,优先级数为ThreadPriority.Normal。
CLR线程池分为工作者线程(workerThreads)与I/O线程(completionPortThreads)两种:
线程池的最大值一般默认为1000、2000。当大于此数目的请求时,将保持排队状态,直到线程池里有线程可用。
使用CLR线程池的工作者线程一般有两种方式:
要注意,不论是通过ThreadPool.QueueUserWorkItem()还是委托,调用的都是线程池里的线程。
通过以下两个方法可以读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。
若想测试线程池中有多少线程正在投入使用,可以通过ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。
方法 | 说明 |
GetAvailableThreads | 剩余空闲线程数 |
GetMaxThreads | 最多可用线程数,所有大于此数目的请求将保持排队状态,直到线程池线程变为可用 |
GetMinThreads | 检索线程池在新请求预测中维护的空闲线程数。 |
QueueUserWorkItem | 启动线程池里得一个线程(队列的方式,如线程池暂时没空闲线程,则进入队列排队) |
SetMaxThreads | 设置线程池中的最大线程数 |
SetMinThreads | 设置线程池最少需要保留的线程数 |
class Program { static void Main(string[] args) { int i = 0; int j = 0; //前面是辅助(也就是所谓的工作者)线程,后面是I/O线程 ThreadPool.GetMaxThreads(out i, out j); Console.WriteLine(i.ToString() + " " + j.ToString()); //默认都是1000 //获取空闲线程,由于现在没有使用异步线程,所以为空 ThreadPool.GetAvailableThreads(out i, out j); Console.WriteLine(i.ToString() + " " + j.ToString()); //默认都是1000 Console.ReadKey(); } }
1、通过QueueUserWorkItem启动工作者线程
ThreadPool线程池中有两个重载的静态方法可以直接启动工作者线程:
先把WaitCallback委托指向一个带有Object参数的无返回值方法,再使用ThreadPool.QueueUserWorkItem(WaitCallback)就可以一步启动此方法,此时异步方法的参数被视为null。
下面来试下用QueueUserWorkItem启动线程池里的一个线程。注意哦,由于是一直存在于线程池,所以不用new Thread()。
class Program { static void Main(string[] args) { //工作者线程最大数目,I/O线程的最大数目 ThreadPool.SetMaxThreads(1000, 1000); //启动工作者线程 ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread)); Console.ReadKey(); } static void RunWorkerThread(object state) { Console.WriteLine("RunWorkerThread开始工作"); Console.WriteLine("工作者线程启动成功!"); } }
输出:
使用第二个重载方法ThreadPool.QueueUserWorkItem(WaitCallback,object)方法可以把object对象作为参数传送到回调函数中。
class Program { static void Main(string[] args) { Person p = new Person(1,"刘备"); //启动工作者线程 ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread), p); Console.ReadKey(); } static void RunWorkerThread(object obj) { Thread.Sleep(200); Console.WriteLine("线程池线程开始!"); Person p = obj as Person; Console.WriteLine(p.Name); } } public class Person { public Person(int id,string name) { Id = id; Name = name; } public int Id { get; set; } public string Name { get; set; } }
输出结果如下:
通过ThreadPool.QueueUserWork启动工作者线程非常方便,但是WaitCallback委托指向的必须是一个带有object参数的无返回值方法。所以这个方法启动的工作者线程仅仅适合于带单个参数和无返回值的情况。
那么如果要传递多个参数和要有返回值又应该怎么办呢?那就只有通过委托了。
2、BeginInvoke与EndInvoke委托异步调用线程
异步调用委托的步骤如下:
class Program { //除了最后两个参数,前面的都是你可定义的 delegate string MyDelegate(string name,int age); static void Main(string[] args) { //建立委托 MyDelegate myDelegate = new MyDelegate(GetString); //异步调用委托,除最后两个参数外,前面的参数都可以传进去 IAsyncResult result = myDelegate.BeginInvoke("刘备",22, null, null); //IAsynResult还能轮询判断,功能不弱 Console.WriteLine("主线程继续工作!"); //调用EndInvoke(IAsyncResult)获取运行结果,一旦调用了EndInvoke,即使结果还没来得及返回,主线程也阻塞等待了 //注意获取返回值的方式 string data = myDelegate.EndInvoke(result); Console.WriteLine(data); Console.ReadKey(); } static string GetString(string name, int age) { Console.WriteLine("我是不是线程池线程" + Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(2000); return string.Format("我是{0},今年{1}岁!",name,age); } }
输出如下:
这种方法有一个缺点,就是不知道异步操作什么时候执行完,什么时候开始调用EndInvoke,因为一旦EndInvoke主线程就会处于阻塞等待状态。
3、IAsyncResult轮询
为了克服上面提到的缺点,此时可以好好利用IAsyncResult提高主线程的工作性能,IAsyncResult有如下成员。
public interface IAsyncResult { object AsyncState {get;} //获取用户定义的对象,它限定或包含关于异步操作的信息。 WailHandle AsyncWaitHandle {get;} //获取用于等待异步操作完成的 WaitHandle。 bool CompletedSynchronously {get;} //获取异步操作是否同步完成的指示。 bool IsCompleted {get;} //获取异步操作是否已完成的指示。 }
示例如下:
class Program { delegate string MyDelegate(string name,int age); static void Main(string[] args) { MyDelegate myDelegate = new MyDelegate(GetString); IAsyncResult result = myDelegate.BeginInvoke("刘备",22, null, null); Console.WriteLine("主线程继续工作!"); //比上个例子,只是利用多了一个IsCompleted属性,来判断异步线程是否完成 while (!result.IsCompleted) { Thread.Sleep(500); Console.WriteLine("异步线程还没完成,主线程干其他事!"); } string data = myDelegate.EndInvoke(result); Console.WriteLine(data); Console.ReadKey(); } static string GetString(string name, int age) { Thread.Sleep(2000); return string.Format("我是{0},今年{1}岁!",name,age); } }
输出如下:
以上例子,除了IsCompleted属性外,还可以使用AsyncWaitHandle如下3个方法实现同样轮询判断效果:
WaitOne:
//比上个例子,判断条件由IsCompleted属性换成了AsyncWaitHandle,仅此而已 while (!result.AsyncWaitHandle.WaitOne(200)) { Console.WriteLine("异步线程没完,主线程继续干活!"); }
WaitAny:
//是否完成了指定数量 WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle }; while (WaitHandle.WaitAny(waitHandleList, 200) > 0) { Console.WriteLine("异步线程完成数未大于0,主线程继续甘其他事!"); }
WaitAll:
WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle }; //是否全部异步线程完成 while (!WaitHandle.WaitAll(waitHandleList, 200)) { Console.WriteLine("异步线程未全部完成,主线程继续干其他事!"); }
4、IAsyncResult回调函数
使用轮询方式来检测异步方法的状态非常麻烦,而且影响了主线程,效率不高。能不能异步线程完成了就直接调用实现定义好的处理函数呢?
有,还是强大的IAsyncResult对象。
class Program { delegate string MyDelegate(string name, int age); static void Main(string[] args) { //建立委托 MyDelegate myDelegate = new MyDelegate(GetString); //倒数第二个参数,委托中绑定了完成后的回调方法 IAsyncResult result1 = myDelegate.BeginInvoke("刘备",23, new AsyncCallback(Completed), null); //主线程可以继续工作而不需要等待 Console.WriteLine("我是主线程,我干我的活,不再理你!"); Thread.Sleep(5000); //Console.ReadKey(); } static string GetString(string name, int age) { Thread.CurrentThread.Name = "异步线程"; //注意,如果不设置为前台线程,则主线程完成后就直接卸载程序了 //Thread.CurrentThread.IsBackground = false; Thread.Sleep(2000); return string.Format("我是{0},今年{1}岁!", name, age); } //供异步线程完成回调的方法 static void Completed(IAsyncResult result) { //获取委托对象,调用EndInvoke方法获取运行结果 AsyncResult _result = (AsyncResult)result; MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate; //获得参数 string data = myDelegaate.EndInvoke(_result); Console.WriteLine(data); //异步线程执行完毕 Console.WriteLine("异步线程完成咯!"); Console.WriteLine("回调函数也是由" + Thread.CurrentThread.Name + "调用的!"); } }
输出如下:
注意:
到目前为止,BeginInvoke("刘备",23, new AsyncCallback(Completed), null)还有最后一个参数没用过的。那么最后一个参数是用来干什么?传参:
namespace 控制台___学习测试 { class Program { delegate string MyDelegate(string name, int age); static void Main(string[] args) { Person p = new Person(2,"关羽"); //建立委托 MyDelegate myDelegate = new MyDelegate(GetString); //最后一个参数的作用,原来是用来传参的 IAsyncResult result1 = myDelegate.BeginInvoke("刘备", 23, new AsyncCallback(Completed), p); //主线程可以继续工作而不需要等待 Console.WriteLine("我是主线程,我干我的活,不再理你!"); Console.ReadKey(); } static string GetString(string name, int age) { Thread.CurrentThread.Name = "异步线程"; //注意,如果不设置为前台线程,则主线程完成后就直接卸载程序了 Thread.CurrentThread.IsBackground = false; Thread.Sleep(2000); return string.Format("我是{0},今年{1}岁!", name, age); } //供异步线程完成回调的方法 static void Completed(IAsyncResult result) { //获取委托对象,调用EndInvoke方法获取运行结果 AsyncResult _result = (AsyncResult)result; MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate; //获得参数 string data = myDelegaate.EndInvoke(_result); Console.WriteLine(data); Person p = result.AsyncState as Person; Console.WriteLine("传过来的参数是:" + p.Name); //异步线程执行完毕 Console.WriteLine("异步线程完成咯!"); Console.WriteLine("回调函数也是由" + Thread.CurrentThread.Name + "调用的!"); } } public class Person { public Person(int id, string name) { Id = id; Name = name; } public int Id { get; set; } public string Name { get; set; } } }
输出如下:
标签:网络连接 返回值 out inter 相对 mint 中断 检索 res
原文地址:https://www.cnblogs.com/Alex80/p/12793230.html