C#多线程编程实战(一):线程基础

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#多线程编程实战(⼀):线程基础
1.1 简介
为了防⽌⼀个应⽤程序控制CPU⽽导致其他应⽤程序和操作系统本⾝永远被挂起这⼀可能情况,操作系统不得不使⽤某种⽅式将物理计算分割为⼀些虚拟的进程,并给予每个执⾏程序⼀定量的计算能⼒。

此外操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。

线程正式这⼀慨念的实现。

多线程优点:可以同时执⾏多个计算任务,有可能提⾼计算机的处理能⼒,使得计算机每秒能执⾏越来越多的命令
多线程缺点:消耗⼤量的操作系统资源。

多个线程共享⼀个处理器将导致操作系统忙于管理这些线程,⽽⽆法运⾏程序。

1.2 创建线程
using System;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(PrintNumbers));//⽆参数的委托
t1.Start();
Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托
t2.Start(10);
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
//注意:要使⽤ParameterizedThreadStart,定义的参数必须为object
static void PrintNumbers(object count)
{
Console.WriteLine("Starting...");
for (int i = 0; i < Convert.ToInt32(count); i++)
{
Console.WriteLine(i);
}
}
}
}
注释:我们只需指定在不同线程运⾏的⽅法名,⽽C#编译器会在后台创建这些对象
1.3 暂停线程
using System;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(PrintNumbersWithDelay);
t1.Start();
PrintNumbers();
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine(i);
}
}
}
}
注释:使⽤Thread.Sleep(TimeSpan.FromSeconds(2));暂停线程
1.4 线程等待
using System;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
t.Join(); //使⽤Join等待t完成
PrintNumbers();
Console.WriteLine("THread Complete");
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine(i);
}
}
}
}
注释:使⽤t.Join(); 等待t完成
1.5 终⽌线程
using System;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting Program...");
Thread t1 = new Thread(PrintNumbersWithDelay);
t1.Start();
Thread.Sleep(TimeSpan.FromSeconds(6));
t1.Abort(); //使⽤Abort()终⽌线程
Console.WriteLine("Thread t1 has been aborted");
Thread t2 = new Thread(PrintNumbers);
PrintNumbers();
Console.ReadLine();
}
static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine(i);
}
}
}
}
注释:使⽤Thread实例的Abort⽅法终⽌线程
1.6 检测线程状态
using System;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Program...");
Thread t1 = new Thread(PrintNumbersWithStatus);
Thread t2 = new Thread(DoNothing);
Console.WriteLine(t1.ThreadState.ToString());//获取实例线程状态 t2.Start();
t1.Start();
for (int i = 0; i < 30; i++)
}
Thread.Sleep(TimeSpan.FromSeconds(6));
t1.Abort();
Console.WriteLine("thread t1 has been aborted");
Console.WriteLine(t1.ThreadState.ToString());
Console.WriteLine(t2.ThreadState.ToString());
Console.ReadLine();
}
private static void PrintNumbersWithStatus()
{
Console.WriteLine("Starting...");
Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine(i);
}
}
private static void DoNothing()
{
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}
注释:使⽤Thread.ThreadState获取线程的运⾏状态。

ThreadState是⼀个C#枚举。

谨记:不要在程序中使⽤线程终⽌,否则可能会出现意想不到的结果1.7 线程优先级
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}");
Console.WriteLine("Running on all cores available");//获取实例线程状态
RunThreads();
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("Running on a single Core");
//让操作系统的所有线程运⾏在单个CPU核⼼上
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
RunThreads();
Console.ReadLine();
}
private static void RunThreads()
{
var sample = new ThreadSample();
var t1 = new Thread(sample.CountNumbers);
= "Thread One";
var t2 = new Thread(sample.CountNumbers);
= "Thread Two";
t1.Priority = ThreadPriority.Highest;//使⽤Priority设置线程的优先级
t2.Priority = ThreadPriority.Lowest;
t1.Start();
t2.Start();
Thread.Sleep(TimeSpan.FromSeconds(2));
sample.Stop();
}
}
class ThreadSample
{
private bool _isStopped = false;
public void Stop()
{
_isStopped = true;
}
public void CountNumbers()
{
long counter = 0;
while (!_isStopped)
{
counter++;
}
Console.WriteLine($"{} with {Thread.CurrentThread.Priority} priority has a count={counter.ToString("N0")}");
}
}
}
注释:单核执⾏多线程耗费的时间⽐多核的多很多
1.8 前台线程和后台线程
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
class Program
{
static void Main(string[] args)
{
var sampleForground = new ThreadSample(10);
var sampleBackground = new ThreadSample(20);
var t1 = new Thread(sampleForground.CountNumbers);
= "ForegroundThread"; //没有明确声明的均为前台线程
var t2 = new Thread(sampleBackground.CountNumbers);
= "BackgroundThread";
t2.IsBackground = true; //设置为后台线程
t1.Start();
t2.Start();
}
}
class ThreadSample
{
private readonly int _iteration;
public ThreadSample(int iteration)
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 0; i < _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{} prints {i}");
}
}
}
}
注释:进程会等待所有的前台线程完成后再结束⼯作,但是如果只剩下后台线程,则会直接结束⼯作
1.9 向线程传递参数
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
ThreadSample sample = new ThreadSample(5);
Thread t1 = new Thread(sample.CountNumbers);
= "ThreadOne";
t1.Start();
t1.Join();
Console.WriteLine("--------------------------");
Thread t2 = new Thread(Count);
= "ThreadTwo";
t2.Start(3);
t2.Join();
Console.WriteLine("--------------------------");
//使⽤lambda表达式引⽤另⼀个C#对⽅的⽅式被称为闭包。

当在lambda表达式中使⽤任何局部变量时,C#会⽣成⼀个类,并将该变量作为该类的⼀个属性,但是我们⽆须定义该类,C#编译器会⾃动帮我们实现 Thread t3 = new Thread(()=> CountNumbers(5));
= "ThreadThree";
t3.Start();
t3.Join();
Console.WriteLine("--------------------------");
int i = 10;
Thread t4 = new Thread(() => PrintNumber(i));
i = 20;
Thread t5 = new Thread(() => PrintNumber(i));
t4.Start();
t5.Start();
//t4, t5都会输出20,因为t4,t5没有Start之前i已经变成20了
Console.ReadKey();
}
static void Count(object iterations)
{
CountNumbers((int)iterations);
}
static void CountNumbers(int iterations)
{
for (int i = 1; i <= iterations; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{} prints {i}");
}
}
static void PrintNumber(int number)
{
Console.WriteLine(number);
}
}
class ThreadSample
private readonly int _iteration;
public ThreadSample(int iteration)
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 1; i <= _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{} prints {i}"); }
}
}
}
注释:也可以使⽤ThreadStart传递参数
1.10 使⽤C# lock关键字
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Incorrect Counter");
Counter c1 = new Counter();
var t1 = new Thread(() => TestCounter(c1));
var t2 = new Thread(() => TestCounter(c1));
var t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total Count: {c1.Count}");
Console.WriteLine("------------------------");
Console.WriteLine("Correct counter");
CounterWithLock c2 = new CounterWithLock();
t1 = new Thread(() => TestCounter(c2));
t2 = new Thread(() => TestCounter(c2));
t3 = new Thread(() => TestCounter(c2));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine($"Total count:{c2.Count}");
Console.ReadLine();
}
static void TestCounter(CounterBase c)
{
for (int i = 0; i < 100000; i++)
{
c.Increment();
c.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 _asyncRoot = new object();
public int Count { get; private set; }
public override void Decrement()
{
lock (_asyncRoot)
{
Count--;
}
}
public override void Increment()
{
lock (_asyncRoot)
{
Count++;
}
}
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
}
class ThreadSample
{
private readonly int _iteration;
public ThreadSample(int iteration)
{
_iteration = iteration;
}
public void CountNumbers()
{
for (int i = 1; i <= _iteration; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine($"{} prints {i}");
}
}
}
}
注释:不加锁,得出的结果不确定,竞争条件下很容易出错。

加锁得出的结果是正确的,但是性能受到了影响1.11 使⽤Monitor类锁定资源
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
object lock1 = new object();
object lock2 = new object();
new Thread(() => LockTooMuch(lock1, lock2)).Start();
lock (lock2)
{
Thread.Sleep(1000);
Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
//直接使⽤Monitor.TryEnter, 如果在第⼆个参数之前还未获取到lock保护的资源会返回false
if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
{
Console.WriteLine("Acquired a protected resource successfully");
}
else
{
Console.WriteLine("Timeout acquiring a resource");
}
}
new Thread(() => LockTooMuch(lock1, lock2)).Start();
Console.WriteLine("-----------------------------");
/* 下⾯代码会造成死锁,所以注释掉
lock (lock2)
{
Console.WriteLine("This will be a deadlock!");
Thread.Sleep(1000);
lock (lock1)
{
Console.WriteLine("Acquired a protected resource successfully");
}
}
*/
}
static void LockTooMuch(object lock1, object lock2)
{
lock (lock1)
{
Thread.Sleep(1000);
lock (lock2);
}
}
}
}
注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁
1.12 处理异常
using System;
using System.Diagnostics;
using System.Threading;
namespace MulityThreadNote
{
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(FaultyThread);
t.Start();
try
{
t = new Thread(BadFaultyThread);
t.Start();
}
catch (Exception ex)
{
Console.WriteLine("We won't get here");
}
}
static void BadFaultyThread()
{
Console.WriteLine("Starting a faulty thread.....");
Thread.Sleep(TimeSpan.FromSeconds(2));
//这个异常主线程⽆法捕捉到,因为是在⼦线程抛出的异常。

需要在⼦线程中加⼊try...catch捕获异常throw new Exception("Boom!");
}
static void FaultyThread()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(TimeSpan.FromSeconds(1));
throw new Exception("Boom");
}
catch (Exception ex)
{
Console.WriteLine($"Exception handled: {ex.Message}");
}
}
}
}
注释:。

相关文档
最新文档