• 线程 方面笔记01


    参考:https://blog.csdn.net/qq_35040828/article/details/73123996

    1. 基础语法 

      

        static void Main()
        {
            Thread t = new Thread(WriteY);
            t.Start();
        }
        static void WriteY()
        {
            while (true)
                Console.Write("y");
        }
    

    2.线程安全

    1)排他锁

    解决方法 提供一个排他锁

        static bool done;
        static object locker;
        static void Main()
        {
            new Thread(Go).Start();
            Go();
        }
        static void Go()
        {
            lock (locker)
            {
                if (!done)
                {
                    Console.WriteLine("Done");
                    done = true;
                }
            }
        }
    

      停止线程的方法  :  锁  、 或sleep   、或 用join方法等待另一个线程结束 

    2)暂停或者sleep

    写在调用方法里 ?      

    Thread.Sleep(TimeSpan.FromSeconds(30));

    3) joint方法 等待另一个线程结束:

    Thread t = new Thread(Go);         
     //Assume Go is some static method
    t.Start();
    t.Join();                            
    //Wait (block) until thread t ends

     3.创建和开始使用多线程

    线程用Thread类来创建, 通过ThreadStart委托来指明方法从哪里开始运行,下面是ThreadStart委托如何定义的:

    public delegate void ThreadStart();

    1)创建方法

    下面是一个例子,使用了C#的语法创建TheadStart委托:

    Thread t = new Thread (new ThreadStart(Go));
    t.Start();  

    一个线程可以通过C#堆委托简短的语法更便利地创建出来:

    Thread t = new Thread(Go);    
    //No need to explicitly use ThreadStart t.Start();

     还有一种方法是使用匿名的方法来启动

     Thread t = new Thread(delegate(){ Console.WriteLine ("Hello!"); });
     t.Start();

     线程有一个IsAlive属性,在调用Start()之后直到线程结束之前一直为true。一个线程一旦结束便不能重新开始了。

    2)将数据传入ThreadStart中

    threadStart  不接受参数

    ParameterizedThreadStart 可以接收一个单独的object类型参数

    public delegate void ParameterizedThreadStart (object obj);

    例如:

    static void Main()
    {
         Thread t = new Thread(go);
         t.start(true);
         //go(true)
         go(false);
    }
    static void go(object upperCase)
    {
        bool upper = (bool)upperCase;
        Console.writeLine(upper?“Hello”:"hello");
    }

      在整个例子中,编译器自动推断出ParameterizedThreadStart委托,因为Go方法接收一个单独的object参数,就像这样写:

    Threadt = new Thread(new ParameterizedThreadStart (Go));
    t.Start(true);

    ParameterizedThreadStart的特性是在使用之前,我们必须对我们想要的类型(例如bool)进行装箱 操作,并且 只能接收一个参数,一个

    static void Main()
     {
        Thread t = new Thread(delegate(){ WriteText ("Hello");});
        t.Start();
    }
    static void WriteText(string text)
     { Console.WriteLine (text); }

    优点是目标方法可以接收任意数量的参数,并且没有装箱操作。

    不过如果将一个外部变量放入匿名方法中

     例如:

    static void Main()
     {
      string text= "Before";
      Thread t = new Thread(delegate() { WriteText (text); });
      text= "After";
      t.Start();
    }
    static void WriteText(string text)
     { Console.WriteLine (text); }

    当外部变量被后来的部分修改了值的时候,可能会透过外部变量进行无意的互动,外部变量最好被处理成只读的,除非添加锁。

    另一个方法是将对象实例的方法而不是静态方法传入到线程中,对象实例的属性告诉线程要做什么:

    class ThreadTest
     {
      bool upper;
      
      static void Main()
     {
        ThreadTest instance1 = new ThreadTest();
        instance1.upper= true;
        Thread t = new Thread(instance1.Go);
        t.Start();
        ThreadTest instance2 = new ThreadTest();
        instance2.Go();       
    //主线程——运行 upper=false
      }
      
      void Go()
     { Console.WriteLine (upper ? "HELLO!" :"hello!");
     }

     4.命名线程

    线程可以通过name属性进行 命名,线程的名字可以在任意时候进行设置,但是只能设置一次 ,崇明会引发异常。

    程序的主线程也可以被命名,例:

            static void Main()
            {
                Thread.CurrentThread.Name = "main";
                Thread work = new Thread(go);
                work.Name = "worker";
                work.Start();
                go();
                Console.ReadLine();
    
    
            }
            static void go ()
            {
                Console.WriteLine("Hello from " + Thread.CurrentThread.Name);
            }

    5.前台和后台线程

    线程默认为前台线程。

    后台线程,当所有的前台线程结束之后,不维持程序的存活

    IsBackground 属性控制它的前后台状态,例:

            static void Main(string []args)
            {
                Thread worker = new Thread(delegate () { Console.ReadLine(); });
                if (args.Length > 0) worker.IsBackground = true;
                worker.Start();
            }

       如果程序被调用的时候没有任何参数,工作线程为前台线程,并且将等待ReadLine语句来等待用户的触发回车,这期间,主线程退出,但是程序保持运行,因为一个前台线程仍然活着。

       另一方面如果有参数传入Main(),工作线程被赋值为后台线程,当主线程结束程序立刻退出,终止了ReadLine。

    后台线程终止 的这种方式 ,使任何最后的操作都 被规避了 ,这种方式是不太合适的,应该等后台工作线程完成后再结束程序 ,可以使用timeout(或大部分时候使用Threa.join)。

    如何因为某种原因,某个线程无法完成,可以用试图终于它的方式,如果失败了再抛弃线程,允许它与进行一起消亡?

    拥有一个后台工作线程是有益的,最直接的理由是:当结束程序的时候它总是可能有最后 的发言权,交互不会消亡的前台线程,保证程序的正常退出。抛弃一个前台工作线程是尤为危险的,尤其是对windowform程序 ,因为我程序直到主线程结束时才退出,但是进程依然运行着。在Windows任务管理器它将从应用程序栏消失不见,但却可以在进程栏找到它。除非用户找到并结束它,它将继续消耗资源,并可能阻止一个新的实例的运行从开始或影响它的特性。

    对于程序失败退出的普遍原因就是存在“被忘记”的前台线程。

    6.线程的优先级

    线程的priority 属性确定了线程 相对于其他同一进程的活动的线程拥有多少执行时间,以下是级别:

    enum ThreadPriority{ Lowest,BelowNormal,normal,AboveNormal,Highest }

    只有有多个线程同时为活动时,优先级才有作用 。

    设置 一个线程的优先级为高一些 ,并不意味着它能执行实时的工作,因为它受限于程序 的进程的级别。要执行实时的工作,必须 提升在System.Diagnostics 命名空间下Process的级别,像下面这样:

    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

    ProcessPriorityClass.High 其实是一个短暂缺口的过程中的最高优先级别:Realtime。设置进程级别到Realtime通知操作系统:你不想让你的进程被抢占了。如果你的程序进入一个偶然的死循环,可以预期,操作系统被锁住了,除了关机没有什么可以拯救你了!基于此,High大体上被认为最高的有用进程级别。

       如果一个实时的程序有一个用户界面,提升进程的级别是不太好的,因为当用户界面UI过于复杂的时候,界面的更新耗费过多的CPU时间,拖慢了整台电脑。(虽然在写这篇文章的时候,在互联网电话程序Skype侥幸地这么做, 也许是因为它的界面相当简单吧。) 降低主线程的级别、提升进程的级别、确保实时线程不进行界面刷新,但这样并不能避免电脑越来越慢,因为操作系统仍会拨出过多的CPU给整个进程。最理想的方案是使实时工作和用户界面在不同的进程(拥有不同的优先级)运行,通过Remoting或共享内存方式进行通信,共享内存需要Win32 API中的 P/Invoking。(可以搜索看看CreateFileMapping  MapViewOfFile)

    7.异常处理

    任何线程创建范围内 try/catch/finally块,当线程开始执行便不再与其有任何关系。

            static void Main()
            {
                try
                {
                    new Thread(go).Start();
                }
                catch (Exception ex)
                {
                    //不会在这里得到异常
                    Console.WriteLine("Exception!");
                    throw;
                }
    
            }
            static void go()
            {
                throw null;
            }

    这里try/catch语句一点用也没有,新创建的线程将引发NullReferenceException异常。当你考虑到每个线程有独立的执行路径的时候,便知道这行为是有道理的

    补救方法是在线程处理的方法中加入自己的异常处理:

            public static void Main()
            {
                new Thread(Go).Start();
            }
    
            static void Go()
            {
                try
                {
                    throw null;
                    //这个异常在下面会被捕捉到...
                }
                catch (Exception ex)
                {
                    //记录异常日志,并且或通知另一个线程 我们发生错误
                }
            }

    从.NET 2.0开始,任何线程内的未处理的异常都将导致整个程序关闭,这意味着忽略异常不再是一个选项了。因此为了避免由未处理异常引起的程序崩溃,try/catch块需要出现在每个线程进入的方法内,至少要在产品程序中应该如此。对于经常使用“全局”异常处理的Windows Forms程序员来说,这可能有点麻烦,像下面这样:

    using System;
    using System.Threading;
    using System.Windows.Forms;
      
    static class Program
     {
      static void Main()
     {
        Application.ThreadException+= HandleError;
        Application.Run (new MainForm());
      }
      
      static void HandleError(object sender,ThreadExceptionEventArgs e) 
    {
        记录异常或者退出程序或者继续运行...
      }
    }

    Application.ThreadException事件在异常被抛出时触发,以一个Windows信息(比如:键盘,鼠标活着 "paint" 等信息)的方式,简言之,一个Windows Forms程序的几乎所有代码。虽然这看起来很完美,它使人产生一种虚假的安全感——所有的异常都被中央异常处理捕捉到了。由工作线程抛出的异常便是一个没有被Application.ThreadException捕捉到的很好的例外。(在Main方法中的代码,包括构造器的形式,在Windows信息开始前先执行)

    .NET framework为全局异常处理提供了一个更低级别的事件:AppDomain.UnhandledException,这个事件在任何类型的程序(有或没有用户界面)的任何线程有任何未处理的异常触发。尽管它提供了好的不得已的异常处理解决机制,但是这不意味着这能保证程序不崩溃,也不意味着能取消.NET异常对话框。

    在产品程序中,明确地使用异常处理在所有线程进入的方法中是必要的,可以使用包装类和帮助类来分解工作来完成任务,比如使用BackgroundWorker类(在第三部分进行讨论)

  • 相关阅读:
    oracle聚合函数及行专列,pivot rollup cube
    oracle expdp导入时 提示“ORA-39002: 操作无效 ORA-39070: 无法打开日志文件 ”
    PL/SQL 美化器不能解析文本
    PL/SQL TOAD 不安装Oracle客户端连接数据库的方法
    oracle 某一字段取反
    jqgrid 加按钮列
    扩展方法 DataTable的ToList<T>
    jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding
    jquery ajax调用WCF,采用System.ServiceModel.WSHttpBinding协议
    学习WCF笔记之二
  • 原文地址:https://www.cnblogs.com/yuejian/p/10930567.html
Copyright © 2020-2023  润新知