线程

Thread类

中的方法

start开始

json等待

前后台线程之分:前台线程结束后后台线程自动结束

  static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(CallBackWotkItem);
            ThreadPool.QueueUserWorkItem(CallBackWotkItem, 1);
            Thread.Sleep(3000);
            Console.WriteLine("主线程退出");
             
        }
        private static void CallBackWotkItem(object start)
        {
            
            Console.WriteLine($"线程开始执行{Thread.CurrentThread.ManagedThreadId}");
            if (start != null)
            {
                Console.WriteLine($"线程池线程ID{Thread.CurrentThread.ManagedThreadId} 狀態{start.ToString()}");
            }
            else
            {
                Console.WriteLine($"线程池线程ID{Thread.CurrentThread.ManagedThreadId} ");
            }
        }

线程池ThreadPool他使用线程都是后台线程,就是线程的容器,当给一个任务时候,线程池调度一个线程用来执行任务

 

   static void Main(string[] args)
        {

            Console.WriteLine("主线程原型");
            CancellationTokenSource cts = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(callback, cts.Token);
            Console.WriteLine("按下回車鍵取消操作");
            Console.Read();
            cts.Cancel();
            Console.ReadKey();
        }
        static void callback(object state)
        {
            CancellationToken token = (CancellationToken)state;
            Console.WriteLine("计数开始");
            Count(token, 1000);

        }
        static void Count(CancellationToken token, int countto)
        {
            for (int i = 0; i < countto; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("计数取消");
                    return;
                }
                Console.WriteLine($"计数为{i}");
                Thread.Sleep(300);
            }
            Console.WriteLine("计数完成");
        }

使用CancellationTokenSource 下的CancellationToken 可以看到线程状态

    class Program
    {
        static int tickets = 100;
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(SaleThread1);
            Thread thread2 = new Thread(SaleThread2);
            thread1.Start();
            thread2.Start();
            Thread.Sleep(4000);
            Console.ReadKey();
            /*线程1出票100
            线程2出票99
            线程2出票97
            线程2出票96
            线程2出票95
            线程2出票94
            线程2出票93
            线程2出票92
            线程2出票91
            线程2出票90
            线程2出票89
            线程2出票88
            线程2出票87
            线程2出票86
            线程2出票85
            线程2出票84
            线程2出票83
            线程2出票82
            线程2出票81
            线程2出票80
            线程2出票79
            线程2出票78
            线程2出票77
            线程2出票76
            线程2出票75
      */
    /*这个地方的执行结果为什么不会产生同样的数据
*/

        }

        static void SaleThread1()
        {
            while (true)
            {
                if (tickets > 0)
                {
                    Console.WriteLine("线程1出票" + tickets--);

                }
                else
                {
                    break;
                }
            }
        }
        static void SaleThread2()
        {
            while (true)
            {
                if (tickets > 0)
                {
                    Console.WriteLine("线程2出票" + tickets--);
                }
                else
                {
                    break;
                }
            }
        }
    }

结果不是按照顺序100 99这种顺序执行的,为什么会产生这种结果?
这种结果不是我们想要的当买票的时候不能第98张还没有卖出去,97就已经卖完这不合理

    class Program
    {
        static int tickets = 100;
        static object glpalOBj = new object();
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(SaleThread1);
            Thread thread2 = new Thread(SaleThread2);
            thread1.Start();
            thread2.Start();
            Thread.Sleep(4000); 
            /*线程1出票100
                线程2出票99
                线程2出票98
                线程2出票97
                线程1出票96
                线程1出票95
                线程2出票94
                线程2出票93
                线程1出票92
                线程1出票91
                线程1出票90
                线程1出票89
                线程2出票88
                线程2出票87
                线程1出票86
                线程1出票85
                线程1出票84
                线程2出票83
                线程2出票82
                线程1出票81
                线程1出票80
                线程2出票79
                线程2出票78
                线程2出票77
                线程2出票76
                线程1出票75*/
        }

        static void SaleThread1()
        {
            while (true)
            {
                try
                {
                    Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁
                    Thread.Sleep(1);
                    if (tickets > 0)
                    {
                        Console.WriteLine("线程1出票" + tickets--);
                    }
                    else
                    {
                        break;
                    }
                }
                finally
                {
                    Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖
                } 
            }
        }
        static void SaleThread2()
        {
            while (true)
            {
                try
                {
                    Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁
                    Thread.Sleep(1);
                    if (tickets > 0)
                    {
                        Console.WriteLine("线程2出票" + tickets--);
                    }
                    else
                    {
                        break;
                    }
                }
                finally
                {
                    Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖
                }
            }
        }
    }
Monitor.Enter(glpalOBj);锁定对象必须要为引用对象,不能为值对象,他加锁Enter和释放锁Exit方法必须是同一个对象,
当使用值类型的时候,会产生一个装箱的操作转换为object对象,解锁的时候也会装箱产生一个object对象,他们两个对象不一样
就会引发SynchronizationLock.Exception异常
代码分析
线程一开始运行执行Monitor.Enter方法,获得glpalOBj排他锁,然后线程一等待,线程二开始执行Monitor.Enter,但是glpalOBj
因为线程1已经加锁没有释放,所以线程2等待。知道线程1执行完成 Monitor.Exit(glpalOBj);释放完毕glpalOBj对象上的排他
锁,线程2才能执行。这样两个线程轮训执行完成了售票工作
但是现在没有解释 try finally语句。现在去除这个语句看看是什么情况

class Program
    {
        static int tickets = 100;
        static object glpalOBj = new object();
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(SaleThread1);
            Thread thread2 = new Thread(SaleThread2);
            thread1.Start();
            thread2.Start();
            Thread.Sleep(4000);
            /*线程1出票100
                线程2出票99
                线程2出票98
                线程2出票97
                线程1出票96
                线程1出票95
                线程2出票94
                线程2出票93
                线程1出票92
                线程1出票91
                线程1出票90
                线程1出票89
                线程2出票88
                线程2出票87
                线程1出票86
                线程1出票85
                线程1出票84
                线程2出票83
                线程2出票82
                线程1出票81
                线程1出票80
                线程2出票79
                线程2出票78
                线程2出票77
                线程2出票76
                线程1出票75*/
        }

        static void SaleThread1()
        {
            while (true)
            {

                Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁
                Thread.Sleep(1);
                if (tickets > 0)
                {
                    Console.WriteLine("线程1出票" + tickets--);
                }
                else
                {
                    break;
                }

                Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖 
            }
        }
        static void SaleThread2()
        {
            while (true)
            { 
                Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁
                Thread.Sleep(1);
                if (tickets > 0)
                {
                    Console.WriteLine("线程2出票" + tickets--);
                }
                else
                {
                    break;
                } 
                Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖

            }
        }
    }

你会发现程序一直不退出,这是什么原因那,当程序开始售票知道没有票的时候

,这时候线程2开始执行  break 语句,线程1要执行Monitor.Enter(glpalOBj);语句但是因为线程二没有释放

glpalOBj对象上的排他锁,所以线程1会一致等待,线程1是前台线程所以程序不会退出,这就是我们经常说的假死状态

CPU分片
CPU在某一个时间点上确实只能执行一个线程,但是多线程不是由于多核或者双核才叫多线程。是由于,很多个线程在并行执行的时候,CPU根据一定的线程调度算法,频繁的进行线程切换,当正在zd执行的一个线程需要进行IO操作或者版需要访问内存的时候,
CPU完全可以放弃该线程,转而调度线程就绪队列上的其他线程,被放弃的线程则进入阻塞状态,IO操作或者访问内存操作结束之后,该线程权可以进入线程就绪队列上。
在程序设计的时候应该避免使用线程同步,因为他会引起一些问题
他使用比较繁琐。我们要用额外的代码把多线程同事访问的数据保卫起来,并获取和释放线程的同步锁。如果在一个代码框忘记获取锁,就可以造成数据损坏
使用线程锁同步会影响程序性能,因为获取和释放锁是需要时间的,并且决定那个线程先获取锁cpu也必须进行一个调度,这会导致线程阻塞。这些额外的工作
对性能造成一定的影响
线程同步每次只允许一个线程访问资源。这会导致线程柱塞。继而系统需要创建更多的线程,CPU也就需要负担更多的调度工作。这个工作也会给性能造成影响
        static void Main(string[] args)
        {
            int x = 0;
            const int iterationNumber = 50000000;
            Stopwatch sw = new Stopwatch();
            for (int i = 0; i < iterationNumber; i++)
            {
                x++;
            }
            Console.WriteLine($"不使用锁的时间{sw.ElapsedMilliseconds}");
            sw.Restart();
            for (int i = 0; i < iterationNumber; i++)
            {
                Interlocked.Increment(ref x);
            }
            Console.WriteLine($"使用锁的时间{sw.ElapsedMilliseconds}");
            Console.ReadKey();
            /*不使用锁的时间0
            使用锁的时间449
            */
        }

 

 



posted @ 2020-05-19 16:43  向往的人生  阅读(207)  评论(0编辑  收藏  举报