一个C#程序起始于一个由】C【】LR与操作系统创建的一个单线程,并且可以通过创建其他的线程来实现多线程编程。
一、基本概念
1、进程:进程是系统资源分配和资源调度的基本单位,每个独立执行的程序在系统中都是一个进程。如qq、word都是一个进程。
2、线程:线程是进程中的执行流程,一个进程中可以包含多个线程,每个线程也可以得到一个小段程序的执行时间。
3、线程调度:
多线程是被CLR内部的线程调度所管理的,这是CLR代表操作系统的一个典型性功能。线程调度能够保证所有的活动的线程都被安排了可行的操作时间,并且线程的等待和挂起并不消耗CPU的资源。在单处理器的计算机上,一个线程调度操控着时隙表,使每个活动线程间的操作能够快速地转换。在一个多处理器计算机上,多线程将在最短时间内被执行,因为不同的线程将在不同的处理器上运行,但是它还是存在特定的时间片。
4、线程和进程的关系
在一个单独的应用程序中,所有的线程都被一个进程以一定的逻辑包含着。进程也是依赖于时间片的概念。
进程完全独立于其他进程的,而线程则与其他同一应用程序中的线程共享存储区。
二、线程管理
1、启动线程
Thread t1=new Thread(方法名);//创建一个线程
t1.Start();//启动该线程
2、线程休眠
sleep在给出的时间区间内阻断。
多线程中,为了使某个线程暂停一段时间
Thread.Sleep(5000);//当前线程暂停5s
3、合并线程
join等待另一个线程来结束。
join方法用于把指定的线程合并到当前线程。如果一个线程T1在执行过程中需要等待另个一个线程T2结束后才能继续执行。
Thread t1=new Thread(port1);//创建一个线程t1
t1.Start();//启动该线程
Thread t2=new Thread(port2);//创建一个线程t2
t2.Start();//启动该线程
void port1()
{
t2.Join();//中断线程1,执行线程2,等待线程2终止,线程1才能继续执行
}
void port1()
{
}
4、线程终止
if(t1.ThreadState==ThreadState.Running)//判断线程是否在执行
{
t1.Abort();//终止线程
}
5、线程的优先级
在多任务操作系统中,每个线程都会得到一小段CPU时间片进行执行,在时间结束时,将轮换另一个线程进入执行状态,这时会选择与当前线程优先级相同的进程进行执行。
如优先级下 :
线程5:线程A和线程B
线程4:线程C
...
线程1:线程D
执行过程:优先级为5的线程A首先的得到CPU时间片,当改时间结束后,轮换到与线程A相同优先级的线程B,当线程B的运行时间结束后,会继续轮换到线程A,当线程A和线程B 都执行完后,才会轮到执行线程B.
Thread t1=new Thread(port1);//创建一个线程t1
t1.Priority= ThreadPriority.Lowest;//设置优先级最低
t1.Start();//启动该线程
Thread t2=new Thread(port2);//创建一个线程t2
t2.Priority= ThreadPriority.Highest;//设置优先级最高
t2.Start();//启动该线程
6、线程同步机制
为了防止多线程中,发送两个线程抢占资源的问题,引入线程同步机制,线性同步指并发线程高效、有序的访问执行,所谓同步,是指某一时刻只有一个线程可以访问资源,只有资源所拥有者放弃了代码或资源的拥有权时,其他线程才可以使用这些资源。
(1)使用lock关键字实现线程同步
lock关键字可以用来确保代码完成运行,而不被其它线程中断。
Object thisLock =new Object();
lock(thisLock)//参数必须基于引用类型对象
{
//要运行的代码块
}
(2)使用monitor类实现线程同步
monitor类提供了通过向单个线程授予对象来控制对对象的访问,当一个线程拥有对象锁时,其它任何线程都不能获取该所。
Monitor.Enter(obj);//锁定代码块
{
//所要执行的代码块
}
Monitor.Exit(obj);//解锁代码块
7、线程的中断和取消提前释放线程
(1) 中断线程 Interrupt()方法
在一个阻塞的线程上强行调用Interrupt()方法,将会抛出一个ThreadInterruptedException异常。中断一个线程只是将它从现在的等待中释放出来,并不会时线程结束。
(2)取消线程 Abort()方法
一个阻塞的线程可以通过Abort()方法而强行释放资源,将会抛出一个ThreadAbortException异常。
举例:
using System;
using System.Threading;
namespace AbortAndInterruptExp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("------------Interrupt方法执行情况---------------");
Thread t1 = new Thread(DoWork);
t1.Start();
Thread.Sleep(1000);
t1.Interrupt();
t1.Join();
Console.WriteLine("------------Abort方法执行情况---------------");
Thread t2 = new Thread(DoWork);
t2.Start();
Thread.Sleep(1000);
t2.Abort();
}
static void DoWork()
{
for (int i = 0; i < 10; i++)
{
try
{
Console.WriteLine("第" + i + "循环。");
Thread.Sleep(500);
}
catch (ThreadInterruptedException e)
{
Console.WriteLine("第" + i + "循环中,线程被中断,下次循环线程将继续运行。");
}
catch (ThreadAbortException e)
{
Console.WriteLine("第" + i + "循环中,线程被终止,线程将不再继续运行");
}
}
}
}
}
参考: https://www.2cto.com/kf/201505/399380.html
Interrupt()方法与Abort()方法的最大不同在于,当一个线程被调用它们时,如果线程当时不处于阻塞状态,那么Interrupt()会等到下一次阻塞发生时再执行操作,而Abort()方法则在线程执行到的地方抛出一个异常。在没有阻塞的线程中调用Abort()方法将会引发很严重的后果。
8、线程状态判断
if (SaveToFile.ThreadState == ThreadState.Running)//判断线程SaveToFile是否处于运行状态
{
}
三、线程参数传递
上面中创建的线程都不带参数,下面讨论如何传递参数。
参考:C# 多线程参数传递
1、通过实体类传递
//需要执行的线程
public class FunctionClass
{
public static string TestFunction(string name, int age)
{
return name + " 的年龄是:" + age;
}
}
//新建实体类
public class TransitionalClass
{
private string name = string.Empty;
private int age;
public string acceptResults = string.Empty;
public TransitionalClass(string name, int age)
{
this.name = name;
this.age = age;
}
public void TestFunction()
{
acceptResults = FunctionClass.TestFunction(this.name, this.age);
}
}
//执行线程
//实例化ThreadWithState类,为线程提供参数
TransitionalClass tc = new TransitionalClass(" Jack", 42);
// 创建执行任务的线程,并执行
Thread t = new Thread(new ThreadStart(tc.TestFunction));
t.Start();
//获取返回值,通过 tc.acceptResults;
四、线程安全
在多线程编程时。一些敏感数据被多个线程同时访问,此时就使用同步访问技术,保证数据的完整性。
1、Lock
lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。
private static object ojb = new object()
lock(obj)
{
//锁定运行的代码段
}
2、Lock对象的选取
在共享线程中任何一个可见的对象只要它是应用类型,那么都可以用来作为线程同步的对象。 用对象本身或者此对象的类型作为同步化的对象时是不被推荐的。
3、死锁
当有很多代码被写在锁语句中时,则会发生无数的并发操作,也就会使得其他的线程进入阻塞状态。死锁现象就是当两个线程相互等待另一个线程所占用的锁时,这两个线程都无法继续运行下去。
线程 1锁定资源 1尝试获取对资源 2的锁定。 同时,线程 2对资源 2有一个锁,它尝试获取对资源 1的锁。 两个线程永远不会放弃锁,因此发生死锁。
避免死锁的最简单方法是使用超时值。 Monitor类( system.threading.monitor ) 可以在获取锁期间设置超时。
if(Monitor.TryEnter(this, 500))
{
//critical section
}
catch (Exception ex)
{
}
finally
{
Monitor.Exit();
}