5.1 资源访问冲突问题
internal class StateObject
{
private int state = 5;
public void ChangeState()
{
if (state == 5)
{
state++;
Console.WriteLine("state: " + state + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state = 5;
}
}
static void Main(string[] args)
{
StateObject state = new StateObject();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(state.ChangeState);
t.Start();
}
}
解决——加锁
internal class StateObject
{
private Object _lock = new Object();
private int state = 5;
public void ChangeState()
{
lock (_lock)
{
if (state == 5)
{
state++;
Console.WriteLine("state: " + state + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state = 5;
}
}
}
异步委托的方式启动线程
static void Test()
{
Console.WriteLine("Test Started");
Console.WriteLine("Test Running");
Thread.Sleep(3000);
Console.WriteLine("Test Completed");
}
delegate void TestDelegate();
static void Main(string[] args)
{
TestDelegate testDelegate = Test;
testDelegate.BeginInvoke(null, null);
Console.WtiteLine("Main Completed");
}
线程的优先级和线程的状态
线程的优先级
在 Thread 类中,可以设置 Priority
属性,以影响线程的基本优先级,Priority
属性是 ThreadPriority
枚举定义的一个值。定义的级别有:Highest
、AboveNormal
、Normal
、BelowNormal
和 Lowest
。
线程的状态
- 获取线程的状态(Running 还是 Unstarted…),当我们通过调用 Thread 对象的 Start() 方法,可以创建线程,但是调用了
Start()
方法之后,新线程不是马上进入 Running 状态,而是处于 Unstarted 状态,只有当操作系统的线程调度器选择了要运行的线程,这个线程的状态才会修改为 Running 状态。我们使用Thread.Sleep()
方法可以让当前线程休眠进入 WaitSleepJoin 状态。 - 使用 Thread对象的
Abort()
方法可以停止线程。调用这个方法,会在要终止的线程中抛出一个ThreadAbortException
类型的异常,我们可以 try catch 这个异常,然后在线程终止前做一些清理的工作。 - 如果需要等待线程的结束,可以调用 Thread 对象的
Join()
方法,表示把 Thread 加入进来,暂停当前线程,并把它设置为 WaitSleepJoin 状态,直到加入的线程完成为止。
线程池
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(Download);
}
Thrad.Sleep(500);
}
static void Download(Object state)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Downloading...: " + Thread.CurrentThread.ManageThreadId);
Thread.Sleep(100);
}
}
- 使用线程池启动的线程默认是后台线程
- 如果进程的所有前台线程都结束了,所有的后台线程就会停止,不能把入池的线程改为前台线程。
- 不能给入池的线程设置优先级或名称
- 入池的线程只能用于时间较短的任务,如果线程要一直运行,就应该使用
Thread
类创建一个线程。
死锁问题
internal class StateObject
{
private Object _lock1 = new Object();
private Object _lock2 = new Object();
private int state1 = 5;
private int state2 = 5;
public void ChangeState()
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
lock (_lock1)
{
lock (_lock2)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
if (state1 == 5)
{
state1++;
Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state1 = 5;
if (state2 == 5)
{
state2++;
Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state2 = 5;
}
}
}
public void ChangeState()
{
lock (_lock2)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
lock (_lock1)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
if (state1 == 5)
{
state1++;
Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state1 = 5;
if (state2 == 5)
{
state2++;
Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state2 = 5;
}
}
}
}
解决方法——规定相同的拿锁顺序
internal class StateObject
{
private Object _lock1 = new Object();
private Object _lock2 = new Object();
private int state1 = 5;
private int state2 = 5;
public void ChangeState()
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
lock (_lock1)
{
lock (_lock2)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
if (state1 == 5)
{
state1++;
Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state1 = 5;
if (state2 == 5)
{
state2++;
Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state2 = 5;
}
}
}
public void ChangeState()
{
lock (_lock1)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
lock (_lock2)
{
Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
if (state1 == 5)
{
state1++;
Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state1 = 5;
if (state2 == 5)
{
state2++;
Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
}
state2 = 5;
}
}
}
}
使用Thread启动线程和传输数据
启动线程
static void Test()
{
Console.WriteLine("Test Started");
Console.WriteLine("Test Running");
Thread.Sleep(1000);
Console.WriteLine("Test Completed");
}
static void Main(string[] args)
{
Thread t = new Thread(Test);
t.Start();
Console.WriteLine("Main Completed");
}
使用 Lambda 表达式:
static void Main(string[] args)
{
Thread t = new Thread(() => Console.WriteLine("Child Thread: " + Thread.CurrentThread.ManageThreadId));
t.Start();
Console.WriteLine("Main Completed: " + Thread.CurrentThread.ManageThreadId);
}
使用匿名方法:
static void Main(string[] args)
{
Thread t = new Thread(delegate () {
Console.WriteLine("Child Thread: " + Thread.CurrentThread.ManageThreadId)
});
t.Start();
Console.WriteLine("Main Completed: " + Thread.CurrentThread.ManageThreadId);
}
传递数据
static void Download(Object x)
{
string str = o as string;
Console.WriteLine(str);
}
static void Main(string[] args)
{
Thread t = new Thread(Download);
t.Start("http://www.xxx.com");
}
Start()
方法只能接收无参的方法或只有一个参数的方法
传递多个数据
public struct Data
{
public string message;
public int age;
}
static void Download(Object x)
{
Data data = (Data)o;
Console.WriteLine(data.message);
Console.WriteLine(data.age);
}
static void Main(string[] args)
{
Data data = new Data();
data.message = "";
data.age = 12;
Thread t = new Thread(Download);
t.Start(data);
}
任务
static void Main(string[] args)
{
TaskFactory tf = new TaskFactory();
Task t = tf.StartNew(Test);
Thread.Sleep(5000);
}
static void Test()
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("A");
}
}
static void Main(string[] args)
{
Task t = new Task(Test);
t.Start();
Thread.Sleep(5000);
}
static void Test()
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("A");
}
}
连续任务
如果一个任务t1的执行是依赖于另一个任务t2的,那么就需要在这个任务t2执行完毕后才开始执行t1。这个时候我们就可以使用连续任务。
static void FirstDownload()
{
Console.WriteLine("Downloading ...");
Thread.Sleep(2000);
}
static void SecondAlert(Task t)
{
Console.WriteLine("下载完成!");
}
static void Main(string[] args)
{
Task t1 = new Task(FirstDownload);
t1.ContinueWith(SecondAlert);
t1.Start();
Thread.Sleep(5000);
}
static void FirstDownload()
{
Console.WriteLine("Downloading ...");
Thread.Sleep(2000);
}
static void SecondAlert(Task t)
{
Console.WriteLine("下载完成!");
}
static void Main(string[] args)
{
Task t1 = new Task(FirstDownload);
Task t2 = t1.ContinueWith(SecondAlert);
Task t3 = t2.ContinueWith(SecondAlert);
t1.Start();
Thread.Sleep(5000);