1. Abort的非及时性

使用多线程经常会遇到一个问题,如何停止这个Thread?在WPF中提供了Abort方法,但MSDN却告诉我们:

线程不一定会立即中止,或者根本不中止。 如果线程在作为中止过程的一部分被调用的 finally 块中做非常大量的计算,从而无限期延迟中止操作,则会发生这种情况。

先看下面一段代码:

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
public partial class MainWindow : Window
{
Thread thread;
public MainWindow()
{
InitializeComponent();
Test t = new Test();
thread = new Thread(new ThreadStart(t.Run));
thread.Start();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("abort");
thread.Abort();
}
}
public class Test
{
public void Run()
{
while (true)
{
Console.WriteLine("Running...");
}
}
}

在运行中点击Button,则可能得到的输出如下:

可以看到Abort后线程仍在运行,然后才停止,输出的结果是多变的,有可能Abort之后再无输出,也有可能输出多行,但都能说明一个问题:使用abort不能立即终止一个thread.

那么到这里,需要先认识一下究竟Abort是什么?

简单来说,Abort是一个标识或者信号,调用它会引发ThreadAbortException,Abort会去请求终止thread,但这不是立即的。

改动一下代码再看看结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("abort");
thread.Abort();
Console.WriteLine(thread.IsAlive);
Console.WriteLine(thread.ThreadState);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Console.WriteLine(thread.IsAlive);
Console.WriteLine(thread.ThreadState);
}

点击Button执行Abort后立即输出thread的状态,发现IsAlive=True,ThreadState=Running,当然也有可能是IsAlive=False,ThreadState=Aborted,因为Abort是不稳定的。然后停一段时间确保thread被终止再点击Button1输出thread的状态,此时IsAlive=False,ThreadState=Aborted.

2. Abort和Join同时使用的意义

由于abort之后获取到的thread的信息是不稳定的,所以可能会想到用Join,它的作用是:

使用此方法确保线程已终止。 如果线程不终止,则调用方将无限期阻塞。 如果调用 Join 时该线程已终止,此方法将立即返回。

也就是说如果我们使用Abort之后再加上Join,那么Join之后的thread肯定是Aborted.

修改一下Button的代码:

1
2
3
4
5
6
7
8
9
10
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("abort");
thread.Abort();
thread.Join();
Console.WriteLine(thread.IsAlive);
Console.WriteLine(thread.ThreadState);
}

没有加入Join之前,输出的IsAlive和ThreadState可能是多种情况的,加入之后,输出的一定是IsAlive=False,ThreadState=Aborted.

需要注意的是Join的作用并不是终止thread,它只是等待thread直到thread执行完成。

3. 如何停止线程

如果考虑让一个thread先停止,然后需要的时候再执行,那么使用abort是很不明智的。一般来说只有当应用程序退出,我们才需要把thread彻底关闭,也就是这时候才用到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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public partial class MainWindow : Window
{
Thread thread;
Test t;
public MainWindow()
{
InitializeComponent();
t = new Test();
thread = new Thread(new ThreadStart(t.Run));
thread.Start();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
t.Flag = !t.Flag;
}
}
public class Test
{
private bool _flag;
public bool Flag
{
set
{
_flag = value;
if (_flag == true)
{
Console.WriteLine("flag=true");
}
else
{
Console.WriteLine("flag=false");
}
}
get
{
return _flag;
}
}
public void Run()
{
while (true)
{
if (Flag)
{
Console.WriteLine("Running...");
}
}
}
}

上面的例子通过信号量来控制thread中的内容是否执行,在实际应用中,如果一个thread中的内容可能再也不会用到,那么对于这一类情况,使用abort和join;如果这个thread只是临时停止,还会再用到,那么使用信号控制就可以了。