创建线程

创建线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Print
{
public static void PrintNumbers()
{
Console.WriteLine("PrintNumbers 方法调用 ********************");
for (var i=0; i<10; i++)
{
Console.WriteLine("PrintNumbers: " + i);
}
}
}
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(Print.PrintNumbers);
Thread s = new Thread(Print.PrintNumbersWithDelay);

Console.WriteLine("s 线程 调用:PrintNumbersWithDelay");
s.Start();

Console.WriteLine("t 线程 调用:PrintNumbers");
t.Start();

Console.WriteLine("主 线程 调用:PrintNumbers");
Print.PrintNumbers();

Console.ReadLine();
}
}
  • 正在执行中的程序实例可被称为一个进程。
  • 进程由一个或多个线程组成
  • 意味着当运行程序时,始终有一个执行程序代码的主线程
  • 当我们构造一个线程时,ThreadStart 或 ParameterizedThreadStart 的实例委托会传给构造函数,我们只需指定在不同线程中运行的方法名,C#编译器后台创建这些对象。

创建线程多种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/// <summary>
/// 创建线程的方式
/// </summary>
class CreateThread
{
/// <summary>
/// 不带参数的委托
/// </summary>
public void CreateThreadWithThreadStart()
{
Thread thread = new Thread(new ThreadStart(ThreadCallBack));
thread.Start();
}
/// <summary>
/// 带参数的委托
/// </summary>
public void CreateThreadWithParamThreadStart()
{
Thread thread = new Thread(new ParameterizedThreadStart(ThreadCallBackWithParam));
Object param = null;
thread.Start(param);
}
/// <summary>
/// 匿名函数
/// </summary>
public void CreateThreadWithAnonymousFunction()
{
Thread thread = new Thread(delegate()
{
Console.WriteLine("进入子线程1");
for (int i = 1; i < 4; ++i)
{
Thread.Sleep(50);
Console.WriteLine("\t+++++++子线程1+++++++++");
}
Console.WriteLine("退出子线程1");
});
thread.Start();
}
/// <summary>
/// 直接赋值委托
/// </summary>
public void CreateThreadWithCallBack()
{
Thread _hThread = new Thread(ThreadCallBack);
_hThread.Start();

}
/// <summary>
/// 无参数的方法调用
/// </summary>
public void ThreadCallBack()
{
// Do Something
}
/// <summary>
/// 带参数的方法
/// </summary>
/// <param name="obj"></param>
public void ThreadCallBackWithParam(object obj)
{
// Do Something
}
}

暂停线程终止线程

暂停线程、线程等待、终止线程

暂停线程

调用 Thread.Sleep 方法会导致当前线程立即阻止,阻止时间的长度等于传递给 Thread.Sleep 的毫秒数,这样,就会将其时间片中剩余的部分让与另一个线程。 一个线程不能针对另一个线程调用 Thread.Sleep。
1
2
3
4
5
6
7
8
9
10
11
12
public class Print
{
public static void PrintNumbersWithDelay()
{
Console.WriteLine("PrintNumbersWithDelay 方法调用 **************");
for (var i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("PrintNumbersWithDelay: " + i);
}
}
}

线程等待

1
2
3
//修改代码如下
s.Start();
s.Join();
  • s.Join() 方法,可以实现多个线程间同步执行

终止线程

永久地停止托管线程。一旦线程被中止,它将无法重新启动。
1
2
3
4
5
//修改代码如下
//s.Join();
Thread.Sleep(TimeSpan.FromSeconds(6));
s.Abort();
Console.WriteLine("s 线程被关闭!");
  • s.Abort() 方法,给线程注入了 ThreadAbortException 方法,导致线程被终结
不推荐使用 Abort 方法来关闭线程
原因:该异常可以在任何时候发生,并可能彻底摧毁应用程序
     该技术不一定总能终止线程,目标线程可以通过处理该异常并调用 Thread.ResetAbort 方法拒绝被终止

线程状态

线程状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Print
{
public static void PrintNumbersWithStatus() {
Console.WriteLine("PrintNumbersWithStatus 方法调用 **************");
Console.WriteLine("\t" +
Thread.CurrentThread.ThreadState.ToString());
for (var i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("PrintNumbersWithStatus: " + i);
}
}
}
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(Print.PrintNumbers);
Thread d = new Thread(Print.PrintNumbersWithDelay);
Thread s = new Thread(Print.PrintNumbersWithStatus);

GetThreadStatus(t, d, s);

Console.WriteLine("d 线程 调用:PrintNumbersWithDelay");
d.Start();

GetThreadStatus(t, d, s);

Console.WriteLine("s 线程 调用:PrintNumbersWithStatus");
s.Start();

//线程等待
//s.Join();

GetThreadStatus(t, d, s);

//线程终止
Thread.Sleep(TimeSpan.FromSeconds(6));
s.Abort();
Console.WriteLine("s 线程被关闭!");

GetThreadStatus(t, d, s);

Console.WriteLine("t 线程 调用:PrintNumbers");
t.Start();

GetThreadStatus(t, d, s);

Console.WriteLine("主 线程 调用:PrintNumbers");
Print.PrintNumbers();

GetThreadStatus(t, d, s);
Console.ReadLine();
}

private static void GetThreadStatus(Thread t, Thread d, Thread s) {
Console.WriteLine("\n\tt.ThreadState: " + t.ThreadState.ToString());
Console.WriteLine("\td.ThreadState: " + d.ThreadState.ToString());
Console.WriteLine("\ts.ThreadState: " +
s.ThreadState.ToString() + "\n");
}
}
注意:不要在程序中使用线程终止,以上代码只是演示线程状态

线程优先级

线程优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class ThreadSample
{
private bool _isStopped = false;

public void Stop() {
_isStopped = true;
}

public void CountNumbers() {
long counter = 0;
while (!_isStopped) {
counter++;
}

Console.WriteLine("{0} with {1,11} priority" +
" has a count = {2,13}",
Thread.CurrentThread.Name,
Thread.CurrentThread.Priority,
counter.ToString("N0"));
}
}


public class Sample {
public static void RunThread() {
ThreadSample s = new ThreadSample();

Thread threadOne = new Thread(s.CountNumbers);
threadOne.Name = "ThreadOne";

Thread threadTwo = new Thread(s.CountNumbers);
threadTwo.Name = "ThreadTwo";

threadOne.Priority = ThreadPriority.Highest;
threadTwo.Priority = ThreadPriority.Lowest;

threadOne.Start();
threadTwo.Start();

Thread.Sleep(TimeSpan.FromSeconds(2));

s.Stop();
}
}

static void Main(string[] args)
{
Console.WriteLine("当前线程的优先级:{0}", Thread.CurrentThread.Priority);
Console.WriteLine("Running on all cores available");

Sample.RunThread();

Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("Running on a single core");
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);

Sample.RunThread();

Console.ReadLine();
}
//输出结果
当前线程的优先级:Normal
Running on all cores available
ThreadOne with     Highest priority has a count = 1,033,257,770
ThreadTwo with      Lowest priority has a count =   778,827,478
Running on a single core
ThreadOne with     Highest priority has a count = 11,519,127,196
ThreadTwo with      Lowest priority has a count =    48,656,315

前、后台线程

前、后台线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
static void Main(string[] args)
{
ThreadForeBackground();
Console.ReadLine();
}

class ThreadSample
{
private bool _isStopped = false;

public void Stop()
{
_isStopped = true;
}

private readonly int _iterations;

public ThreadSample() { }

public ThreadSample(int iterations)
{
_iterations = iterations;
}

public void CountNumbers()
{
long counter = 0;
while (!_isStopped)
{
counter++;
}

Console.WriteLine("{0} with {1,11} priority" +
" has a count = {2,13}",
Thread.CurrentThread.Name,
Thread.CurrentThread.Priority,
counter.ToString("N0"));
}

public void PrintInterations()
{
for (var i = 0; i < _iterations; i++)
{
Console.WriteLine("{0} prints {1}",
Thread.CurrentThread.Name, i);
}
}
}

private static void ThreadForeBackground() {
try {
ThreadSample sampleFore = new ThreadSample(10);
ThreadSample sampleBack = new ThreadSample(20);

Thread one = new Thread(sampleFore.PrintInterations);
one.Name = "ForegroundThread";
Thread two = new Thread(sampleBack.PrintInterations);
two.Name = "BackgroundThread";

two.IsBackground = true;

one.Start();
two.Start();
}
catch (Exception ex) {
LogHelper._multithreadingLog.Info("ThreadForeBackground: {0}", ex);
}
}
  • 要点:

    1、当在主线程中创建了一个线程,那么该线程的 IsBackground 默认是设置为 FALSE 的。

    2、当主线程退出的时候,IsBackground = FALSE 的线程还会继续执行下去,直到线程执行结束。

    3、只有 IsBackground = TRUE 的线程才会随着主线程的退出而退出。

    4、当初始化一个线程,把 Thread.IsBackground = true 的时候,指示该线程为后台线程。后台线程将会随着主线程的退出而退出。

    5、原理:只要所有前台线程都终止后,CLR 就会对每一个活在的后台线程调用 Abort()来彻底终止应用程序。

线程传参

线程传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Print
{
public static void PrintNumbers(object number)
{
Console.WriteLine("PrintNumbers 方法调用 ********************");
int numb = number != null? (int)number: 10;
for (var i = 0; i < numb; i++)
{
Console.WriteLine("PrintNumbers: " + i);
}
}

public static void PrintNumbers(int number) {
Console.WriteLine("带参数的 PrintNumbers 方法调用 ***************");
for (var i = 0; i < number; i++)
{
Console.WriteLine("PrintNumbers: " + i);
}
}
}

//Start 接受 类型为 Object 的单个参数
Thread a = new Thread(Print.PrintNumbers);
a.Start(20);

//实例,构造器传参
ThreadSample sampleFore = new ThreadSample(10);
Thread b = new Thread(sampleFore.PrintInterations);
b.Name = "sampleFore 线程传参:";
b.Start();

//Lambda 传参
Thread c = new Thread(() => Print.PrintNumbers(15));
c.Start();

死锁、Monitor 类

锁定、死锁、Monitor 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private static void MonitorLock() {
object lock1 = new object();
object lock2 = new object();

new Thread(() => LockTooMuch(lock1, lock2)).Start();

lock (lock2) {
Thread.Sleep(1000);

new Thread(() => Print.PrintNumbers(6)).Start();

if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
{
Console.WriteLine("Monitor.TryEnter 获取成功!");
}
else {
Console.WriteLine("Monitor.TryEnter 获取失败!");
}
}

Console.WriteLine("--------------------");
//new Thread(() => LockTooMuch(lock1, lock2)).Start();

lock (lock2)
{
Thread.Sleep(1000);
Console.WriteLine("这将会形成一个死锁!");
lock (lock1) {
Console.WriteLine("lock 获取成功!");
}
}
}

private static void LockTooMuch(Object lock1, Object lock2) {
lock (lock1) {
Thread.Sleep(1000);
lock (lock2) {
Console.WriteLine("线程运行...");
};
}
}

lock 本质

private object o = new object();
public void Work()
{
  object temp = o;
  System.Threading.Monitor.Enter(temp);
  try
  {
    //做一些需要线程同步的工作
  }
  finally
  {
    System.Threading.Monitor.Exit(temp);
  }
}    
提供给 lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围。
最好避免锁定 public 类型或锁定不受应用程序控制的对象实例
常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:

1)如果实例可以被公共访问,将出现 lock (this) 问题。
2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。
3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。
最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。