线程的学习与使用

  1. 一、线程的简单创建

 可以把要执行的方法当做参数传递给线程,还有一种就是通过Lambda表达式匿名函数去直接创建。

static void Test()

{

///直接把方法传递给线程

Thread thread = new Thread(Test);

thread.Start();

 

///匿名函数创建线程

Thread th2 = new Thread(() =>

{

Console.WriteLine("匿名创建");

});

th2.Start();

Console.ReadKey();

Console.WriteLine("方法");

 

}

  

  1. 线程的一些简单操作

    nterrupt()。这个方法作用是唤醒沉睡的线程。当线程沉睡的时候,调用Interrupt()方法,这会把当前沉睡的线程唤醒,并抛出ThreadInterruptedException

   

 //唤醒线程
Thread thread = new Thread(() =>
{
Console.WriteLine("线程开始啦");
Console.WriteLine("睡着啦");
try
{
Thread.Sleep(50000);
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine("被叫醒啦");
}
Console.WriteLine("醒啦");
});
thread.Start();
Console.WriteLine("按任意键唤醒线程");
Console.ReadKey();
thread.Interrupt();
Console.ReadKey();

 

 

   

 

三、线程的一些小细节

    Thread.Sleep();//休眠线程,即让当前的线程休眠指定的时间。

    Thread.Abort ();//终止线程,即抛出一个ThreadAbortException

    线程的参数化:

    当执行下面的语句我们想要输出的值为5但实际输出的却为6,因为在线程还未执行完成的时候又重新给i赋值为6,所以当线程执行的时候输出i的值就为6了。若想让线程输出指定的值的话,就得为该线程传递参数进去,给线程指定

默认的参数。

 

 线程创建的时候默认的为非后台线程。当所有的非后台线程全部执行完后,进程才会关闭,否则看起来主线程关闭了,但是该进程却并没有关闭,依旧在后台运行着等待着其余非后台线程的执行。

把线程设置为后台线程就不会出现上述的情况了,把线程设置为"后台线程"后,所有"非后台线程"执行结束后程序就会退出,不会去等后台程序是否执行完。设置后台线程:thread.IsBackground

线程的优先级:

    thread.Priority = ThreadPriority.Normal;//线程创建时的默认级别

    thread.Priority = ThreadPriority.Highest;//最高优先级

    thread.Priority = ThreadPriority.Lowest;//最低优先级

    thread.Priority = ThreadPriority.BelowNormal;//Normal之后Lowest之前

    thread.Priority = ThreadPriority.AboveNormal;//Highest之后在Normal

四、线程同步

    下面程序按理来说输出的值应该为10000才对,但是输出的却不是10000.原因很简单就是因为当thread线程在操作count的时候,thread2也在操作count。当threadcount赋值之后,thread2重新再为count赋值就把count的值重新又给初始化

了,所以count的值输出的就不为10000

           线程同步就是解决多个线程同时操作一个资源的问题。下面代码就是简单的实现线程同步。使用lock去解决多线程的问题,lock C#中的关键字,他要锁定一个资源,lock 的特点是:同时只能有一个线程进入 lock 的对象的范围,

其他 lock 的线程就要等。就是多个线程同一时候只能有一个线程去执行被标记锁的对象。

int count = 0;

Thread thread = new Thread(() =>

{

lock (Lock)//添加锁 设置线程锁的对象同一时间只能有一个线程去操作它

{

for (int i = 0; i < 5000; i++)

{

count++;

Thread.Sleep(1);

}

}

});

 

Thread thread2 = new Thread(() =>

{

lock (Lock)//添加锁

{

for (int j = 0; j < 5000; j++)

{

count++;

Thread.Sleep(1);

 

}

}

});

thread2.Start();

thread.Start();

while (thread.IsAlive) { }

while (thread2.IsAlive) { }//等待线程结束

Console.WriteLine(count);

 

五、Monitor

lock关键字相当于对Monitor的调用。Monitor类的作用就是把一个对象给锁起来,若有线程在用的话就等待对方释放解锁,然后自己用的时候再把这个对象给锁起来。Monitor的三个最常用的方法就是。

Monitor.Enter();//为指定对象加上锁

Monitor.Exit(Lock);//释放锁,释放对象,解锁。

Monitor.TryEnter();这个方法放回的是一个bool类型,若指定的对象被锁定之后,它不会去等待释放,而是返回false。下面就是对Monitor的简单应用。

   
 public static Object Lock = new object();//设置线程锁的对象同一时间只能有一个线程去操作它
        static void Main(string[] args)
        {
            int count = 0;
            Thread thread = new Thread(
                () =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        try
                        {
                            Monitor.Enter(Lock);//加上锁
                            count++;
                            Console.WriteLine("开始借钱");
                            Thread.Sleep(1000);
                            Console.WriteLine("借了" + count);

                    
                        }
                        finally
                        {
                            Monitor.Exit(Lock);//释放锁 
                        }
                    }

                    
                }
                );
            Thread thread2 = new Thread(
                () =>
                {

                    

                    for (int j = 0; j < 100; j++)
                    {
                        try
                        {
                            Monitor.Enter(Lock);//加上锁
                            count++;
                            Console.WriteLine(DateTime.Now.ToString() + "--开始还钱");
                            Thread.Sleep(1000);
                            Console.WriteLine(DateTime.Now.ToString() + "还了" + count);                          
                        }
                        finally
                        {
                            Monitor.Exit(Lock);//释放锁 
                        }

                    }

                }
                );
            thread.Start();
            thread2.Start();
            thread.Join();
            thread2.Join();
        }
 

 

六、单例模式练习

1、饿汉模式

public class GetOne

{
//单例模式之饿汉模式
private static GetOne test = new GetOne();
private GetOne()//构造函数私有化
{

}
public static GetOne GetOnly()
{
return test;
}

}

 

2、懒汉模式加上线程的同步

public class GetOne
    {
        private static Object Lock = new object();//创建锁对象
        //单例模式之懒汉式
        private static GetOne test = null;
        private GetOne()//构造函数私有化
        {

        }
        public static GetOne GetOnly()
        {
            if (test == null)//判断GetOne是否为null。多加上这个判断是为了减少上锁的次数
            {
                lock (Lock)//若为null就加上锁去创建,防止多个线程同时创建
                {
                    if (test == null)
                    {
                        test = new GetOne();
                    }

                }
            }
            return test;
        }

    }

 

 

七、线程池

ThreadPool.QueueUserWorkItem();//里面传入委托。还可以在后面添加一个Object的参数,这个参数的意义就是实现线程的参数化。

    WaitHandle 线程间的通讯。他有两个子类是经常用到的。

    ManualResetEvent:这个就相当于与是手动去操作。把线程间的通讯表示成开门关门的话。当创建 ManualResetEvent man=new ManualResetEvent(false)

;这个类的时候,false就代表初始化的时候就已经关上门了。当线程中调用 man.WaitOne();就带表了这个线程一直在等待(WaitOne()可以添加参数,表示等待的时间)

等待着开门。当线程调用man.Set();的时候,该线程就开门了,线程就会继续往下执行。man.Reset();手动关门

    第二个子类是AutoResetEvent。这个就是在执行WaitOne()线程等待的时候,若发现开门之后,当执行完线程后会自动把门给关闭。

posted @ 2020-01-22 10:32  落幕无痕  阅读(133)  评论(0编辑  收藏  举报