• 线程


    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
                */
            }
     



  • 相关阅读:
    081、Weave Scope 多主机监控(2019-04-29 周一)
    080、Weave Scope 容器地图(2019-04-28 周日)
    079、监控利器 sysdig (2019-04-26 周五)
    078、Docker 最常用的监控方案(2019-04-25 周四)
    077、跨主机使用Rex-Ray volume (2019-04-24 周三)
    076、创建Rex-Ray volume (2019-04-23 周二)
    075、配置Virtualbox backend(2019-04-22 周一)
    074、如何安装和配置Rex-Ray?(2019-04-19 周五)
    073、如何实现跨Docker Host 存储? (2019-04-18 周四)
    072、一文搞懂各种Docker网络 (2019-04-17 周三)
  • 原文地址:https://www.cnblogs.com/-alvin/p/12787015.html
Copyright © 2020-2023  润新知