<一>Thread 线程基础

一、新建一个控制台,创建线程,并启动线程(start)

Thread t = new Thread(PrintfNum);
t.Start();
PrintfNum();

static void PrintfNum()
{
    Console.WriteLine("启动");
    for (int num = 0; num < 10; num++)
    {
        Console.WriteLine(num);
    }
}

上述方法启动了一个子线程去运行PrintfNum方法,主程序本身也执行了一次PrintfNum。来执行看看结果,两个启动一起显示了,说明子线程是和主线程是同时运行的。

二、线程等待(Join)

Thread t = new Thread(PrintfNumWait);
t.Start();
t.Join();
PrintfNum();

static void PrintfNumWait()
{
    Console.WriteLine("启动延迟方法");
    Thread.Sleep(2000);
}

当主线程的后续操作需要等待线程执行完成后再运行时,就应该进行等待,如上述方法,等待线程2秒后,再进行打印数字。

三、线程终止(abort),线程等待(Suspend),线程继续(Resume) 已经被弃用。

一个线程在终止时会强制中断线程的执行,不管方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象。这一现象会被其它因为请求锁而阻塞的线程看到,使他们继续向下执行。这就会造成数据的不一致。

一个线程被挂起时不会破换对象和强制释放锁,相反它会一直保持对锁的占有,如果不使用Resume方法唤醒,就会造成死锁。

四、检测线程状态(ThreadState)

Thread t = new Thread(PrintfNumWait);
t.Start();
for (int i = 0; i < 5; i++)
{
    Thread.Sleep(1000);
    Console.WriteLine(t.ThreadState.ToString());
}

static void PrintfNumWait()
{
    Console.WriteLine("启动延迟方法");
    Thread.Sleep(2000);
}

五、线程优先级:CPU核心大部分时间在运行高优先级的线程,只留给剩下的线程很少的时间来运行。所以最高优先级的线程通常会计算更多的迭代

Thread t = new Thread(PrintfNumWait);
t.Priority = ThreadPriority.AboveNormal;
t.Start();
Lowest,                  最低级别
BelowNormal,         低于一般
Normal,                 一般线程
AboveNormal,         高于一般
Highest                  最高级别

六、前后、后台线程

默认情况下,显式创建的线程是前台线程。通过手动的设置thread对象的IsBackground 属性为ture来创建一个后台线程。进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作。
一个重要注意事项是如果程序定义了一个不会完成的前台线程,主程序并不会正常结束。

Thread t = new Thread(PrintfNumWait);
t.Start();
Thread t1 = new Thread(PrintfNum);
t1.IsBackground = true;
t1.Start();

七、向线程传递参数

线程的参数传递由多种方式,常用的有全局变量,start()传参,另一种是lambda表达式。lambda表达式时一个闭包,解析时会解析成一个类,利用类的构造函数接收参数,然后用类全局变量来提供给线程使用。

Thread t = new Thread(PrintfNum);
TestModel m=new TestModel ();
m.count = 2;
m.name = "test";
t.Start(m);

Thread t2 = new Thread(() => PrintfNum(m));
t2.Start();

static void PrintfNum(object model)
{
    TestModel m = (TestModel)model;
    for (int i = 0; i < m.count; i++)
    {
        Console.WriteLine(m.name+i);
    }
}

public class TestModel
{
   public int count { get; set; }
   public  string name { get; set; }
}

八、锁(lock)

当多个线程同时访问一个资源时,会产生资源竞争。导致数据在多线程环境中经常出现数据返回错误。

compute c=new compute ();
c.sleep = 3500;
Thread t1 = new Thread(() => PrintfNum(c));
c.sleep = 2200;
Thread t2 = new Thread(() => PrintfNum(c));
c.sleep = 1600;
Thread t3 = new Thread(() => PrintfNum(c));
t1.Start();
t2.Start();
t3.Start();
 void PrintfNum(compute model)
{
    for (int i = 0; i < 10; i++)
    {
        model.Couter();
        Console.WriteLine(model.num);
    }
}
class compute
{
    public int sleep { get; set; }
    public int num { get; set; }
    public  void Couter() {   
            num++;
            Thread.Sleep(sleep);
            num--;
    }
}

如上代码,一个线程执行一次printfNum 最终的打印结果应该都是0.但由于中间执行了等待后,由于num是共享的,会被其他线程修改,导致打印出来的数据有很多错误的。

为了确保不会发生以上情形,必须保证当有线程操作compute对象时,所有其他线程必须等待直到当前线程完成操作。我们可以使用lock 关键字来实现这种行为。如果锁定了一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待直到该对象解除锁定。在for循环添加lock,如下,就能保证每次打印的都是0了。

object ss = new object();
void PrintfNum(compute model)
{
    for (int i = 0; i < 10; i++)
    {
        lock (ss)
        {
            model.Couter();
            Console.WriteLine(model.num);
        }
    }
}

九、使用monitor类锁定资源

由于lock容易产生死锁,那么先来了解一下什么叫死锁。

死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

 

 如下代码:

object locker1 = new object();
object locker2 = new object();
new Thread(()=>Printf(locker1, locker2)).Start();
lock (locker2)
{
    lock (locker1)
    {
    }
}
void Printf(object A,object B)
{
    lock (A)
    {
        Thread.Sleep(1000);
        lock (B)
        {
        }
    }
}

来看看lock的源代码如下,先enter一个资源进行锁定,然后加个try进行异常判定,最后解锁资源。如果在try里面被阻塞死了,那么这个资源就无法走到finally里面了

        Monitor.Enter(this);
        try
        {
          
        }
        finally
        {
            Monitor.Exit(this);
        }

所以lock实际上还是使用的Monitor,而monitor有一个tryenter的方法。它支持接收一个超时时间。这样就可以避免死锁了。

if (Monitor.TryEnter(this, 1000))
{
    //......业务
    Monitor.Exit(this);
}

十、异常处理

多线程只能在线程方法里面进行处理,异常不可以跨线程捕获,也就是说异常不能抛出到主线程获取。

 

posted @ 2022-05-27 23:55  许轩霖  阅读(56)  评论(0编辑  收藏  举报