一、C# Thread类的基本用法

通过System.Threading.Thread类可以开始新的线程,并在线程堆栈中运行静态或实例方法。可以通过Thread类的的构造方法传递一个无参数,并且不返回值(返回void)的委托(ThreadStart),这个委托的定义如下:[ComVisibleAttribute(true)]public delegate void ThreadStart()我们可以通过如下的方法来建立并运行一个线程。

复制代码

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6
7 namespace MyThread
8 {
9 class Program
10 {
11 public static void myStaticThreadMethod()
12 {
13 Console.WriteLine(“myStaticThreadMethod”);
14 }
15 static void Main(string[] args)
16 {
17 Thread thread1 = new Thread(myStaticThreadMethod);
18 thread1.Start(); // 只要使用Start方法,线程才会运行
19 }
20 }
21 }
22
复制代码

除了运行静态的方法,还可以在线程中运行实例方法,代码如下:

复制代码

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6
7 namespace MyThread
8 {
9 class Program
10 {
11 public void myThreadMethod()
12 {
13 Console.WriteLine(“myThreadMethod”);
14 }
15 static void Main(string[] args)
16 {
17 Thread thread2 = new Thread(new Program().myThreadMethod);
18 thread2.Start();
19 }
20 }
21 }
22
复制代码

如果读者的方法很简单,或出去某种目的,也可以通过匿名委托或Lambda表达式来为Thread的构造方法赋值,代码如下:

1 Thread thread3 = new Thread(delegate() { Console.WriteLine(“匿名委托”); });
2 thread3.Start();
3
4 Thread thread4 = new Thread(( ) => { Console.WriteLine(“Lambda表达式”); });
5 thread4.Start();
6

其中Lambda表达式前面的( )表示没有参数。为了区分不同的线程,还可以为Thread类的Name属性赋值,代码如下:

1 Thread thread5 = new Thread(()=>{ Console.WriteLine(Thread.CurrentThread.Name); });
2 thread5.Name = “我的Lamdba”;
3 thread5.Start();

如果将上面thread1至thread5放到一起执行,由于系统对线程的调度不同,输出的结果是不定的,如图1是一种可能的输出结果。

一种可能的输出结果

图1

二、 定义一个线程类

我们可以将Thread类封装在一个MyThread类中,以使任何从MyThread继承的类都具有多线程能力。MyThread类的代码如下:

复制代码

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6 namespace MyThread
7 {
8 abstract class MyThread
9 {
10 Thread thread = null;
11
12 abstract public void run();
13
14 public void start()
15 {
16 if (thread == null)
17 thread = new Thread(run);
18 thread.Start();
19 }
20 }
21 }
22
复制代码

可以用下面的代码来使用MyThread类。

复制代码

1 class NewThread : MyThread
2 {
3 override public void run()
4 {
5 Console.WriteLine(“使用MyThread建立并运行线程”);
6 }
7 }
8
9 static void Main(string[] args)
10 {
11
12 NewThread nt = new NewThread();
13 nt.start();
14 }
15
复制代码

我们还可以利用MyThread来为线程传递任意复杂的参数。详细内容见下节。

三、C# Thread类:为线程传递参数

Thread类有一个带参数的委托类型的重载形式。这个委托的定义如下:[ComVisibleAttribute(false)]public delegate void ParameterizedThreadStart(Object obj)这个Thread类的构造方法的定义如下:

public Thread(ParameterizedThreadStart start);
下面的代码使用了这个带参数的委托向线程传递一个字符串参数:

复制代码

1 public static void myStaticParamThreadMethod(Object obj)
2 {
3 Console.WriteLine(obj);
4 }
5
6 static void Main(string[] args)
7 {
8 Thread thread = new Thread(myStaticParamThreadMethod);
9 thread.Start(“通过委托的参数传值”);
10 }
11
复制代码

要注意的是,如果使用的是不带参数的委托,不能使用带参数的Start方法运行线程,否则系统会抛出异常。但使用带参数的委托,可以使用thread.Start()来运行线程,这时所传递的参数值为null。也可以定义一个类来传递参数值,如下面的代码如下:

复制代码

1 class MyData
2 {
3 private String d1;
4 private int d2;
5 public MyData(String d1, int d2)
6 {
7 this.d1 = d1;
8 this.d2 = d2;
9 }
10 public void threadMethod()
11 {
12 Console.WriteLine(d1);
13 Console.WriteLine(d2);
14 }
15 }
16
17 MyData myData = new MyData(“abcd”,1234);
18 Thread thread = new Thread(myData.threadMethod);
19 thread.Start();
20
复制代码

如果使用在第二节定义的MyThread类,传递参数会显示更简单,代码如下:

复制代码

class NewThread : MyThread
{
private String p1;
private int p2;
public NewThread(String p1, int p2)
{
this.p1 = p1;
this.p2 = p2;
}

override public void run()
{  Console.WriteLine(p1);  Console.WriteLine(p2);
}

}

NewThread newThread = new NewThread(“hello world”, 4321);
newThread.start();

复制代码

四、前台和后台线程

使用Thread建立的线程默认情况下是前台线程,在进程中,只要有一个前台线程未退出,进程就不会终止。主线程就是一个前台线程。而后台线程不管线程是否结束,只要所有的前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止。一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序。下面的代码演示了前台和后台线程的区别。

复制代码

1 public static void myStaticThreadMethod()
2 {
3 Thread.Sleep(3000);
4 }
5
6 Thread thread = new Thread(myStaticThreadMethod);
7 // thread.IsBackground = true;
8 thread.Start();
复制代码

如果运行上面的代码,程序会等待3秒后退出,如果将注释去掉,将thread设成后台线程,则程序会立即退出。要注意的是,必须在调用Start方法之前设置线程的类型,否则一但线程运行,将无法改变其类型。通过BeginXXX方法运行的线程都是后台线程。

五、C# Thread类:判断多个线程是否都结束的两种方法

确定所有线程是否都完成了工作的方法有很多,如可以采用类似于对象计数器的方法,所谓对象计数器,就是一个对象被引用一次,这个计数器就加1,销毁引用就减1,如果引用数为0,则垃圾搜集器就会对这些引用数为0的对象进行回收。方法一:线程计数器线程也可以采用计数器的方法,即为所有需要监视的线程设一个线程计数器,每开始一个线程,在线程的执行方法中为这个计数器加1,如果某个线程结束(在线程执行方法的最后为这个计数器减1),为这个计数器减1。然后再开始一个线程,按着一定的时间间隔来监视这个计数器,如是棕个计数器为0,说明所有的线程都结束了。当然,也可以不用这个监视线程,而在每一个工作线程的最后(在为计数器减1的代码的后面)来监视这个计数器,也就是说,每一个工作线程在退出之前,还要负责检测这个计数器。使用这种方法不要忘了同步这个计数器变量啊,否则会产生意想不到的后果。方法二:使用Thread.join方法join方法只有在线程结束时才继续执行下面的语句。可以对每一个线程调用它的join方法,但要注意,这个调用要在另一个线程里,而不要在主线程,否则程序会被阻塞的。个人感觉这种方法比较好。线程计数器方法演示:

复制代码

1 class ThreadCounter : MyThread
2 {
3 private static int count = 0;
4 private int ms;
5 private static void increment()
6 {
7 lock (typeof(ThreadCounter)) // 必须同步计数器
8 {
9 count++;
10 }
11 }
12 private static void decrease()
13 {
14 lock (typeof(ThreadCounter))
15 {
16 count–;
17 }
18 }
19 private static int getCount()
20 {
21 lock (typeof(ThreadCounter))
22 {
23 return count;
24 }
25 }
26 public ThreadCounter(int ms)
27 {
28 this.ms = ms;
29 }
30 override public void run()
31 {
32 increment();
33 Thread.Sleep(ms);
34 Console.WriteLine(ms.ToString()+“毫秒任务结束”);
35 decrease();
36 if (getCount() == 0)
37 Console.WriteLine(“所有任务结束”);
38 }
39 }
40
41
42 ThreadCounter counter1 = new ThreadCounter(3000);
43 ThreadCounter counter2 = new ThreadCounter(5000);
44 ThreadCounter counter3 = new ThreadCounter(7000);
45
46 counter1.start();
47 counter2.start();
48 counter3.start();
49
复制代码

上面的代码虽然在大多数的时候可以正常工作,但却存在一个隐患,就是如果某个线程,假设是counter1,在运行后,由于某些原因,其他的线程并未运行,在这种情况下,在counter1运行完后,仍然可以显示出“所有任务结束”的提示信息,但是counter2和counter3还并未运行。为了消除这个隐患,可以将increment方法从run中移除,将其放到ThreadCounter的构造方法中,在这时,increment方法中的lock也可以去掉了。代码如:

1 public ThreadCounter(int ms)
2 {
3 this.ms = ms;
4 increment();
5 }

运行上面的程序后,将显示如图2的结果。

图2

图2

使用Thread.join方法演示

复制代码

1 private static void threadMethod(Object obj)
2 {
3 Thread.Sleep(Int32.Parse(obj.ToString()));
4 Console.WriteLine(obj + “毫秒任务结束”);
5 }
6 private static void joinAllThread(object obj)
7 {
8 Thread[] threads = obj as Thread[];
9 foreach (Thread t in threads)
10 t.Join();
11 Console.WriteLine(“所有的线程结束”);
12 }
13
14 static void Main(string[] args)
15 {
16 Thread thread1 = new Thread(threadMethod);
17 Thread thread2 = new Thread(threadMethod);
18 Thread thread3 = new Thread(threadMethod);
19
20 thread1.Start(3000);
21 thread2.Start(5000);
22 thread3.Start(7000);
23
24 Thread joinThread = new Thread(joinAllThread);
25 joinThread.Start(new Thread[] { thread1, thread2, thread3 });
26
27 }
28
复制代码

在运行上面的代码后,将会得到和图2同样的运行结果。上述两种方法都没有线程数的限制,当然,仍然会受到操作系统和硬件资源的限制。

C#线程使用(二)全面总结相关推荐

  1. Linux线程(二)

    linux线程(二) 文章目录 linux线程(二) 一.线程终止 二.线程等待 三.线程分离 四.线程互斥 一.线程终止 1.pthread_exit函数: 只需终止某个线程而不需要终止整个进程的三 ...

  2. python线程(二)代码部分Threading模块

    一.Thread类简介 1.Thread类参数简介 class threading.Thread(group=None, target=None, name=None, args=(), kwargs ...

  3. 创建线程方式二 java 1615474026

    创建线程方式二 java 1615474026 实现Runnable接口方式创建线程 演练 定义一个类,实现接口 测试类

  4. linux线程篇,linux线程篇 (二) 线程的基本操作

    线程 进程 标识符 pthread_t pid_t 获取ID pthread_self() getpid() 创建 pthread_create() fork 销毁 pthread_exit() ex ...

  5. 线程系列二:Thread构造函数

    线程系列二:Thread构造函数 文章目录 线程系列二:Thread构造函数 前言 一.new Thread 二.与jVM 1.与内存的关系 2.线程创建的数量 三.守护线程 前言 在我们开始学线程的 ...

  6. JavaSE-多线程(二)

    JavaSE-多线程(二) 线程控制 get/setPriority() 优先级别设置 join() 插入线程 sleep() 阻塞线程 yield() 线程就绪(唤醒) setDaemon() 伴随 ...

  7. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  8. Python的进程和线程(二)——IO密集型任务

    一.什么是IO密集型任务? 主要的核心任务是进行IO操作,比如写文件,进行磁盘的读写等等. 上一篇博客,对计算密集型任务,多线程并没有体现它的好处,反而话费的时间更长.对IO密集型任务多线程会有明显的 ...

  9. 线程使用二——线程池

    线程池的作用: 线程池作用就是限制系统中执行线程的数量.      根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数 ...

  10. Python 线程(二):简单锁实现线程同步

    Python中有两种锁,一个锁是原始的锁(原语), 不可重入,而另一种锁则是可重入的锁即递归锁.而是thread模块中,只提供了不可重入的锁,而在threading中则提供这两种锁. 可重入:当一个线 ...

最新文章

  1. JDK1.8源码阅读系列之二:LinkedList
  2. OSS全球传输加速开启公测,助力企业业务全地域覆盖...
  3. 《网易编程题》疯狂队列
  4. 开发物体识别桌、_【课程总结】AR系统开发“秘籍”大揭秘!
  5. new java.util.date_java.util.Date的替代品
  6. Silverlight 自定义表格 转
  7. Eclipse选择哪个版本
  8. JavaScript的String对象使用
  9. 爬虫入门【10】Pyspider框架简介及安装说明
  10. 【转】XP系统电脑开机自动登陆系统
  11. 如何删除itunes中的ipa安装包
  12. linux脚本菜鸟教程,菜鸟教程之shell _1
  13. 软件公司是如何招聘人才的?
  14. [ZZ] 为什么中国目前仍然没有一流的三维动画电影
  15. css preserve-3d 使用
  16. 微信后台基于时间序的海量数据冷热分级架构设计实践
  17. 5.2.6UART寄存器编程(下)
  18. JAVA 映射HashMap和HashTable的区别和实用
  19. 观念什么意思_观念是什么意思_观念怎么读_造句_近义词_拼音_解释
  20. 安全事件日志事件编号与描述

热门文章

  1. NeuralFinder:集成人工生命和遗传算法自动发现神经网络最优结构
  2. 25行代码AC_蓝桥杯 2017A组省赛第九题 分巧克力(暴力优化)
  3. wait()和sleep()
  4. golang 1.9 sp MySQL_Golang-使用mysql
  5. 自动图片轮播php源码,js图片自动轮播代码分享(js图片轮播)
  6. Mysql数据库(一)——mysql数据库初体验
  7. shell脚本详解(八)——一键部署自动化装机(PXE无人值守)
  8. html使div内部元素水平排列_元素周期表探讨
  9. python paramiko sftp_Python学习—paramiko模块实现简单的ssh与sftp
  10. mybatis多个foreach_使用 Mybatis 的 foreach 批量模糊 like 查询及批量插入