这篇文章给大家分享的是有关C#异步多线程中Thread的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
这里对 Thread 的一些常用 API 进行介绍,使用一些案例进行说明。由于 Thread 的不可控与效率问题,Thread 现在已经不常用了,这里介绍一些 API ,想更深入的同学可以继续研究研究。
首先看 Thread 的构造函数,有 ThreadStart 、ParameterizedThreadStart 、maxStackSize 类型的参数,这三个常用的也就 ThreadStart ,其他两个可以作为了解。
分别 F12 查看 ThreadStart、ParameterizedThreadStart ,可以看到 ThreadStart 是无参数类型的委托、ParameterizedThreadStart 是有参数类型的委托。maxStackSize 是指定线程占用的最大内存数。
接着我们创建一个简单的案例,启动一个线程,模拟做一些任务行。如下代码
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}"); // 做一些任务 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}"); }; Thread thread = new Thread(threadStart); thread.Start(); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); Console.ReadLine();
启动程序,可以看到线程 1(主线程),没有等待线程 3(子线程)执行完成匿名方法内的任务,再执行 Main 结束这段代码。如果使用的是 winform 是不会卡界面的。
这就是异步多线程,异步在于线程 1 并没有等待线程 3 执行完成任务,再执行线程 1 内的下一行,而是让线程 3 在不影响线程 1 执行任务的情况下执行,这就是异步。多线程在于我们启动了一个线程 3(子线程),在 Main 方法由线程1(子线程)与线程 3(主线程)一起完成 Main 方法内的代码,这就是多线程。
说到委托可会有小伙伴发出疑问,为啥不用 Action ?
因为在这个版本还没有 Action、Func,这是在 .Net 3.0 时代的产物,Action、Func 的出现就是为了统一,也是为了解决此类问题。
在 dotnet 框架,也建议最好使用 Action、Func,所以,在这使用 Action 是不可以的。如下
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); Action action = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}"); // 做一些任务 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}"); }; ThreadStart threadStart = action; Thread thread = new Thread(threadStart); thread.Start(); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); Console.ReadLine();
Suspend 挂起、Resume 唤醒,这两个是一对相互对应的 API,使用时这两个容易产生死锁,其实在实际中也是不应该使用的,.NET 框架已经抛弃了,说的很清楚了。
为什么会死锁呢?比如你开启了一个子线程 01,对 A 文件进行读写操作,此时你对子线程 01 进行了挂起。当你另外一个线程对 02 A 文件进行操作时,此时提示会 A 文件被占用,就行形成死锁。
Abort 销毁,很多人在使用,这种是抛异常方式,使子线程销毁结束。这个功能也比较鸡肋,Abort 时子线程并不能立即停止,往往会有一些延迟,那这个销毁有时也不能达到我们可控的效果。
比如,在一个方法内开了一个子线程进行数据计算,但执行的时间太长了,我们等待了 5000 ms,此时 Abort 子线程,是不能立马让子线程停止计算,而是可能要等一会才能结束子线程。
比如,发出的动作,可能收不回来。查询数据库来说,当一个查库命令发送到数据库,我们在C# 执行了 Abort,但查库这个命令是收不回来的,因为他是在数据库层面,当数据库查询完成只是没有接收响应的线程罢了。
Abort 不建议使用,如果使用,一定要 try catch 一下。
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}"); // 做一些任务 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}"); }; Thread thread = new Thread(threadStart); thread.Start(); try { thread.Abort(); // 销毁,方式是抛异常,不一定及时 } catch (Exception ex) { //Thread.ResetAbort(); // 取消异常 } Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId}"); Console.ReadLine();
Suspend、Resume、Abort 这几个方法不建议使用,操作线程暂停、销毁或者其他操作都是不可控的,应为线程本身是操作系统的, CPU 分时分片会按照自己的规则进行运行,此时已经不是程序可以进行控的了。 既然设计了 Thread 不可能一无是处,接下来我们说些有用的
线程等待 ,Join 可以一直等,也可以设置超时,超时就是等待一定时间,就不等了。等待的过程中主线程处于闲置状态等着子线程完成任务。如果是 winform 是会卡界面的,主线程等待也是一种工作。
例如:threadStart 我们模拟任务耗时 5 秒,在 thread.Start() 任务开始后,使用 thread.Join() 等着子线程完成工作
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Start(); thread.Join(); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.ReadLine();
启动程序,可以看到是我们想要的结果(与同步执行一样),主线程 1 一直等着 子线程 3 完成执行的任务。如果是 winform 是会卡界面的,虽然 thread.Join() 主线程 1 会等着子线程 3 完成工作,但主线程 1 等着也是一种工作。
接着我们看下超时等待,Join 的重载方法
例如:threadStart 我们模拟任务耗时 5 秒,在 thread.Start() 任务开始后,使用 thread.Join(3*1000) ,让主线程最多等子线程 3 秒,如果 3 秒子线程还未完成任务,就不等待了
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Start(); thread.Join(3 * 1000); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.ReadLine();
启动程序,主线程 1 开始任务,子线程 3 也开始任务,当子线程执行 3 s 后(期间主线程 1 在等待),主线程 3 开始执行任务了。
注意:thread.Join(n * 1000) 并不是一定会等待那么长时间,而是最多等待,期间子线程任务执行完成后,就不等待了。
例如:threadStart 任务方法模拟 5 s,thread.Join(7 * 1000) 主线程等待 7 s
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Start(); thread.Join(7 * 1000); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.ReadLine();
线程状态,ThreadState 也可以做线程等待,等待的过程中主线程处于闲置状态等着子线程完成任务。如果是 winform 是会卡界面的,主线程等待也是一种工作。
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Start(); while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); // 当前线程休息 200 毫秒 } Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.ReadLine();
线程暂停,Sleep 当前线程暂停。如果是 winform 是会卡界面的,当 Sleep 时,CPU 分片就交出去了,主线程并不在工作状态。
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.ReadLine();
是否是后台线程,当实例 Thread 时,默认是前台线程(IsBackground == false )。前台线程一定要任务完成,才会让进程退出。后台线程(IsBackground == true)会随着进程的结束而结束,无论子线程任务是否完成。
前台线程,意思也就是,当我们启动一个程序,当关闭程序时,如果还有子线程执行任务,当前进程是不会退出的,会等待着子进程将任务执行完成,也就是会阻止进程结束,反之亦然。
例如:前台线程,启动控制台后,主线程执行完任务后,会等待子线程任务完成(5s)后,窗口才会被关闭
static void Main(string[] args) { Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Start(); while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); // 当前线程休息 200 毫秒 } Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }
例如:后台线程,启动控制台后,主线程任务执行完毕后,窗口会立马被关闭
static void Main(string[] args) { Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.IsBackground = true; thread.Start(); Console.WriteLine($"thread IsBackground:{thread.IsBackground},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }
线程可以设置优先级,当线程从高到低分配了优先级,在向 CPU 申请线程时会优先分配。但是这个功能也比较鸡肋,对于 CPU 而言,当他们同时过来,只是会为优先级高的先分进行分片,但优先级低的并不是不会分配,也不代表优先级高的就会先执行完成,这也取决执行的任务量。其实优先级也没什么用,多线程本来就是无序的。
Console.WriteLine($"Main 方法开始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); ThreadStart threadStart = () => { Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); // 做一些任务 Thread.Sleep(5 * 1000); // 模拟任务耗时 5 秒 Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}"); }; Thread thread = new Thread(threadStart); thread.Priority = ThreadPriority.Highest;// CPU 会先执行,不代表 Highest 就最优先 thread.Start(); Console.WriteLine($"thread IsBackground:{thread.IsBackground},DateTime:{DateTime.Now.ToLongTimeString()}"); Console.WriteLine($"Main 方法结束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
感谢各位的阅读!关于“C#异步多线程中Thread的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。