1. 使用Thread创建线程
创建一个Thread类的实例,传入要在独立线程执行的方法,启用Start()方法
最简单的示例如下:
using System.Threading;
定义一个方法
static void PrintNumbers()
{
Console.WriteLine("Print Numbers Starting....");
for (int i = 1; i < 10; i++)
{
Console.WriteLine($"{i}->ThreadID:{Thread.CurrentThread.ManagedThreadId}");
}
}
在Main方法中测试:
static void Main(string[] args)
{
Console.WriteLine("Main Thread start...");
Console.WriteLine($"MainID: {Thread.CurrentThread.ManagedThreadId}");
Thread t = new Thread(PrintNumbers);
t.Start();
PrintNumbers();
Console.Read();
}
输出:
Main Thread start...
MainID: 1
Print Numbers Starting....
1->ThreadID:1
2->ThreadID:1
Print Numbers Starting....
1->ThreadID:3
2->ThreadID:3
3->ThreadID:3
4->ThreadID:3
5->ThreadID:3
6->ThreadID:3
7->ThreadID:3
8->ThreadID:3
9->ThreadID:3
3->ThreadID:1
4->ThreadID:1
5->ThreadID:1
6->ThreadID:1
7->ThreadID:1
8->ThreadID:1
9->ThreadID:1
从输出数据看,创建的线程(线程ID 3)和主线程(线程ID 3)同时在执行。
在方法中增加延时
static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting....");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.2));
Console.WriteLine($"{i}->ThreadID:{Thread.CurrentThread.ManagedThreadId}");
}
}
再在Main函数中执行测试:
static void Main(string[] args)
{
Console.WriteLine("Main Thread start...");
Console.WriteLine($"MainID: {Thread.CurrentThread.ManagedThreadId}");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
PrintNumbersWithDelay();
Console.Read();
}
输出:
Main Thread start...
MainID: 1
Starting....
Starting....
1->ThreadID:3
1->ThreadID:1
2->ThreadID:3
2->ThreadID:1
3->ThreadID:3
3->ThreadID:1
4->ThreadID:3
4->ThreadID:1
5->ThreadID:3
5->ThreadID:1
6->ThreadID:3
6->ThreadID:1
7->ThreadID:3
7->ThreadID:1
8->ThreadID:3
8->ThreadID:1
9->ThreadID:3
9->ThreadID:1
2个线程同时都在执行打印输出,每个电脑执行的结果可能不完全相同,只是都能反应并行输出。
2. 线程暂停Sleep()方法,线程等待Join()方法
等待线程完成后所输出的结果
例如,计算从1加到100
static void Sum()
{
//int result = 0;
Console.WriteLine("Starting....");
for (int i = 0; i <= 100; i++)
{
result += i;
}
}
在Main函数中测试验证:
private static int result = 0;
static void Main(string[] args)
{
Thread t = new Thread(Sum);
t.Start();
//Thread.Sleep(1000);
Console.WriteLine(result);
t.Join();
Console.WriteLine($"Complteted:{result}");
}
输出:
Starting....
0 //线程开始后,这个值还是0,如果休眠1秒中,情况就不一样了,在主线休眠1秒的时间,独立线程以及计算完成,所以也是5050
Complteted:5050 //等待线程执行完成后的结果
3. 线程优先级
设置线程的ThreadPriority属性
创建一个ThreadSample类,存在一个不停计数的方法和一个停止计数的布尔变量,将类的实例布尔变量设置为false时,停止计数。
class ThreadSample
{
private bool _isStopped = false;
public void Stop()
{
_isStopped = true;
}
public void CountNumbers()
{
long counter = 0;
while(!_isStopped)
{
counter++;
}
Console.WriteLine($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority,11} has a count{counter.ToString("N0"),13}");
}
}
创建一个静态方法来测试线程优先级:
static void RunThreads()
{
var sample = new ThreadSample();
var t1 = new Thread(sample.CountNumbers);
t1.Name = "线程1";
var t2 = new Thread(sample.CountNumbers);
t2.Name = "线程2";
t1.Priority = ThreadPriority.Highest;
t2.Priority = ThreadPriority.Lowest;
t1.Start();
t2.Start();
Thread.Sleep(2000);
sample.Stop();
}
不同的线程优先级,是否计数一样多呢?
Main Thread priority Normal
Running on all cores available
线程1 with Highest has a count 897,909,334
线程2 with Lowest has a count 689,778,768
显然时高优先级执行的迭代次数多,但总体时一致的。
如果使用单核测试,情况就不一样了。
Console.WriteLine("Run on a single core");
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(10);
RunThreads();
输出:
Run on a single core
线程2 with Lowest has a count 838,668,519
线程1 with Highest has a count1,024,629,110
显然不时一个数量级
4. 终止线程,线程的状态
Abort() 方法:该技术不一定总能终止线程,不推荐使用该方法来关闭线程
ThreadState属性值即为线程的状态
测试代码如下:
static void DoNothing()
{
Console.WriteLine("T2 Starting...");
Thread.Sleep(2000);
}
static void PrintNumbersWithStatus()
{
Console.WriteLine($"{Thread.CurrentThread.Name}:{Thread.CurrentThread.ThreadState.ToString()}");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(500);
Console.WriteLine(i);
}
}
static void Main(string[] args)
{
Console.WriteLine("Main Starting ....");
Thread t = new Thread(PrintNumbersWithStatus);
t.Name = "T1 withs status";
Thread t2 = new Thread(DoNothing);
t2.Name = "T2 do nothing";
Console.WriteLine($"主线程状态:{Thread.CurrentThread.ThreadState.ToString()}");
Console.WriteLine($"在主线程中 T1线程状态:{t.ThreadState.ToString()}");
Console.WriteLine($"在主线程中 T2程状态:{t2.ThreadState.ToString()}");
t2.Start();
t.Start();
for (int i = 1; i < 30; i++)
{
Console.WriteLine($"T1:{t.ThreadState.ToString()}");
}
Thread.Sleep(6000);
t.Abort();
Console.WriteLine("T1 Thread has been aborted");
Console.WriteLine($"在主线程中 T1线程状态:{t.ThreadState.ToString()}");
Console.WriteLine($"在主线程中 T2程状态:{t2.ThreadState.ToString()}");
Console.WriteLine($"主线程状态:{Thread.CurrentThread.ThreadState.ToString()}");
Console.ReadLine();
}
输出:
Main Starting ....
主线程状态:Running
在主线程中 T1线程状态:Unstarted
在主线程中 T2程状态:Unstarted
T2 Starting...
T1:Running
T1:Running
T1:Running
T1:Running
T1:Running
T1:Running
T1 withs status:Running
T1:Running
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
T1:WaitSleepJoin
1
2
3
4
5
6
7
8
9
T1 Thread has been aborted
在主线程中 T1线程状态:Aborted
在主线程中 T2程状态:Stopped
主线程状态:Running
5.前台线程和后台线程
显示创建的线程都是前台线程
可通过设置 IsBackground属性设置true来创建后台线程
主要区别:
进程会等待所有前台线程完成后再结束工作,如果只剩下后台线程,则会直接结束工作。
如果程序定义了一个不会完成的前台线程,主程序就不会正常结束。
创建如下示例代码:
class ThreadSample
{
private readonly int _iterations;
public ThreadSample(int iterations)
{
_iterations = iterations;
}
public void CountNumbers()
{
for (int i = 0; i < _iterations; i++)
{
Thread.Sleep(500);
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
}
static void Main(string[] args)
{
var TForegroundSample = new ThreadSample(10);
var TBackgroundSample = new ThreadSample(20);
var t1 = new Thread(TForegroundSample.CountNumbers);
t1.Name = "ForegroundThread";
var t2 = new Thread(TBackgroundSample.CountNumbers);
t2.Name = "BackgroundThread";
t2.IsBackground = true;
t1.Start();
t2.Start();
//Console.Read();
}
2给线程同时执行,但是迭代到9时,前台线程完成,虽然有后线程在执行,所有的前台线程执行完成,不会等待后台线程执行完成的,就直接结束了整个主线程。
6. 向线程传递参数
创建示例代码:
class Demo
{
private readonly int _iterations;
public Demo(int iterations)
{
_iterations = iterations;
}
public void CountNumbers()
{
for (int i = 0; i<=_iterations; i++)
{
Thread.Sleep(500);
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
}
static void CountNumbers(int iterations)
{
for (int i = 0; i <= iterations; i++)
{
Thread.Sleep(500);
Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
}
}
static void Count(object iteratios)
{
CountNumbers((int)iteratios);
}
static void PrintNUmbers(int num)
{
Console.WriteLine(num);
}
static void Main(string[] args)
{
//创建一个演示类的实例,使用构成函数传入值
var Sample = new Demo(10);
var t1 = new Thread(Sample.CountNumbers); //创建线程执行实例的方法
t1.Start();
t1.Join();
Console.WriteLine("---------------------------------");
var t2 = new Thread(Count); //创建现在执行静态方法,参数为Object
t2.Name = "T2";
t2.Start(8); //传入参数,函数会强转为int
t2.Join();
Console.WriteLine("---------------------------------");
var t3 = new Thread(() => CountNumbers(12)); //使用Lambda表达式
t3.Name = "T3";
t3.Start();
t3.Join();
Console.WriteLine("---------------------------------");
int i = 10;
var t4 = new Thread(() => PrintNUmbers(i));
i = 20;
var t5 = new Thread(() => PrintNUmbers(i)); //线程4和5都会打印20,启动线程前变量被修改为20
t4.Start();
t5.Start();
}
输出:
prints 0
prints 1
prints 2
prints 3
prints 4
prints 5
prints 6
prints 7
prints 8
prints 9
prints 10
---------------------------------
T2 prints 0
T2 prints 1
T2 prints 2
T2 prints 3
T2 prints 4
T2 prints 5
T2 prints 6
T2 prints 7
T2 prints 8
---------------------------------
T3 prints 0
T3 prints 1
T3 prints 2
T3 prints 3
T3 prints 4
T3 prints 5
T3 prints 6
T3 prints 7
T3 prints 8
T3 prints 9
T3 prints 10
T3 prints 11
T3 prints 12
---------------------------------
20
20
7.线程加锁
确保当一个线程使用某些资源时,同时其它线程无法使用该资源
确保线程安全
创建2个类,一个加锁,一个不加锁:
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
class Counter : CounterBase
{
public int Count { get; private set; }
public override void Decrement()
{
Count--;
}
public override void Increment()
{
Count++;
}
}
class CounterWithLock : CounterBase
{
private readonly object obj_lock = new object();
public int Count { get; private set; }
public override void Decrement()
{
lock (obj_lock)
{
Count--;
}
}
public override void Increment()
{
lock (obj_lock)
{
Count++;
}
}
}
static void TestCounter(CounterBase c)
{
for (int i = 0; i < 100000; i++)
{
c.Increment();
c.Decrement();
}
}
在Main方法中验证:
static void Main(string[] args)
{
Console.WriteLine("Incorrect Counter");
var c = new Counter();
var t1 = new Thread(() => TestCounter(c));
var t2 = new Thread(() => TestCounter(c));
var t3 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total Count:{c.Count}");
Console.WriteLine("------------------------------");
Console.WriteLine("Correct Counter");
var c1 = new CounterWithLock();
t1 = new Thread(() => TestCounter(c));
t2 = new Thread(() => TestCounter(c));
t3 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total Count:{c1.Count}");
Console.WriteLine("------------------------------");
}
输出:
Incorrect Counter
Total Count:122
------------------------------
Correct Counter
Total Count:0
------------------------------
从结果可以看到出,加锁后输出和预期的一样,执行一次自增,再执行一次自减,理论是0.
但是不加锁的执行结果是122,这值是随机变化的,不确定。