多线程编程基础案例
多线程编程基础案例
一、使用线程的理由
1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。
2、可以使用线程来简化编码。
3、可以使用线程来实现并发执行。
二、基本知识
1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源。
2、前台线程和后台线程:通过Thread类新建线程默认为前台线程。当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。
3、挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程序的执行情况不可预知,所以使用挂起和唤醒容易发生死锁的情况,在实际应用中应该尽量少用。
4、阻塞线程:Join,阻塞调用线程,直到该线程终止。
5、终止线程:Abort:抛出 ThreadAbortException 异常让线程终止,终止后的线程不可唤醒。Interrupt:抛出 ThreadInterruptException 异常让线程终止,通过捕获异常可以继续执行。
6、线程优先级:Highest AboveNormal Normal BelowNormal Lowest ,默认为Normal。
三、线程的使用
线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数),可以用一个类或结构体封装参数。
案例:
class Program { static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(TestMethod));//创建无参数数线程 Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));//创建带参数的线程 //设置为后台进程 t1.IsBackground = true; t2.IsBackground = true; t1.Start(); t2.Start("hello"); Console.ReadKey(); } public static void TestMethod() { Console.WriteLine("不带参数的线程函数"); } //参数要定义为object 类型 public static void TestMethod(object data) { string datastr = data as string; Console.WriteLine("带参数的线程函数,参数为:{0}", datastr); } }
四、线程池
由于线程的创建和销毁需要耗费一定的开销,过多的使用线程会造成内存资源的浪费,出于对性能的考虑,于是引入了线程池的概念。线程池维护一个请求队列,线程池的代码从队列提取任务,然后委派给线程池的一个线程执行,线程执行完不会被立即销毁,这样既可以在后台执行任务,又可以减少线程创建和销毁所带来的开销。
线程池线程默认为后台线程(IsBackground)。
class Program { static void Main(string[] args) { //将工作项加入到线程池队列中,这里可以传递一个线程参数 ThreadPool.QueueUserWorkItem(TestMethod, "Hello"); Console.ReadKey(); } //线程函数 public static void TestMethod(object data) { string datastr = data as string; Console.WriteLine(datastr); } }
五、Task类
使用ThreadPool的QueueUserWorkItem()方法发起一次异步的线程执行很简单,但是该方法最大的问题是没有一个内建的机制让你知道操作什么时候完成,有没有一个内建的机制在操作完成后获得一个返回值。为此,可以使用System.Threading.Tasks中的Task类。
构造一个Task<TResult>对象,并为泛型TResult参数传递一个操作的返回类型。
class Program { static void Main(string[] args) { Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); t.Start(); t.Wait(); Console.WriteLine(t.Result); Console.ReadKey(); } private static Int32 Sum(Int32 n) { Int32 sum = 0; for (; n > 0; --n) checked{ sum += n;} //结果太大,抛出异常 return sum; } }
一个任务完成时,自动启动一个新任务。
一个任务完成后,它可以启动另一个任务,下面重写了前面的代码,不阻塞任何线程。
class Program { static void Main(string[] args) { Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000); t.Start(); //t.Wait(); Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}", t.Result)); Console.ReadKey(); } private static Int32 Sum(Int32 n) { Int32 sum = 0; for (; n > 0; --n) checked { sum += n; } //结果溢出,抛出异常 return sum; } }
六、委托异步执行
委托的异步调用:BeginInvoke() 和 EndInvoke()
namespace Test { public delegate string MyDelegate(object data); class Program { static void Main(string[] args) { MyDelegate mydelegate = new MyDelegate(TestMethod); IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param"); //异步执行完成 string resultstr = mydelegate.EndInvoke(result); } //线程函数 public static string TestMethod(object data) { string datastr = data as string; Console.WriteLine(datastr); return datastr; } //异步回调函数 public static void TestCallback(IAsyncResult data) { Console.WriteLine(data.AsyncState); } } }
异步回调函数在上面线程函数执行结束后,将要退出时执行。
本文来自博客园,作者:码农阿亮,转载请注明原文链接:https://www.cnblogs.com/wml-it/p/14808683.html
技术的发展日新月异,随着时间推移,无法保证本博客所有内容的正确性。如有误导,请大家见谅,欢迎评论区指正!
开源库地址,欢迎点亮:
GitHub:https://github.com/ITMingliang
Gitee: https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang
建群声明: 本着技术在于分享,方便大家交流学习的初心,特此建立【编程内功修炼交流群】,为大家答疑解惑。热烈欢迎各位爱交流学习的程序员进群,也希望进群的大佬能不吝分享自己遇到的技术问题和学习心得!进群方式:扫码关注公众号,后台回复【进群】。