zoukankan      html  css  js  c++  java
  • C#多线程基础

    最近花了近两周时间读完了C#本质论,这本书非常喜欢,但是到后面关于多线程和同步这块,读起来就感觉有些困难了,所以做了笔记,一方面防止忘记,另一方法如果有不正确的地方,十分欢喜各位前辈不吝赐教

    什么是单线程

    通过一个控制台程序来认识单线程

    static void Main(string[] args)
    {
        var mainThread = Thread.CurrentThread;
    }

    在Console.WriteLine处添加一个断点,查看主线程属性

    ApartmentSate:msdn的大致意思,相同对单元状态的线程之间可以相互访问对象,然而在.net中由clr以线程安全的方式管理所有共享资源

    CurrentCulture和CurrentUICulture表示区域信息

    ExecutionContext:封装线程相关的上下文信息

    IsAlive:如果此线程已启动并且尚未正常终止或中止,则为 true;否则为 false

    IsBackground:表示是否是后台线程

    IsThreadPoolThread:表示是否是线程池线程

    ManagedThreadId:托管线程的唯一标识符

    更多可查msdn

    小结:

    关于线程的定义很多地方都有的,我想举一个例子,很多时候,我们人就是一个线程,早上起床,吃早饭,上班,下班......这一系列事情有序执行就是一个单线程,但是有的时候,一边听歌,一边看小说实际上就开启了第二个线程了,假如此时再写代码,那就是开启第三个线程了

    使用Thread创建一个线程

    const int Repetitions = 100;
    static void Main(string[] args)
    {
        ThreadStart threadStart = DoWork;
        Thread thread = new Thread(threadStart);
        thread.Start();
        //Main线程启动一个循环
        for (int count = 0; count < Repetitions; count++)
        {
            Console.Write('-');
        }
        Console.WriteLine("(主线程最后一个语句...)");
    }
    static void DoWork()
    {
        for (int count = 0; count < Repetitions; count++)
        {
            Console.Write("+");
        }
    }

    Ctrl+F5运行,可以看到,新创建的线程和Main线程中的循环是同步执行的(多启动几次,会有不一样的发现哦!)

    那么问题来了,我们创建的线程执行完了吗?程序到底什么时候结束?为什么主线程最后一句话执行完了,创建的线程还在控制台输出?

    修改一下程序,Ctrl+F5,多启动几次,会有不一样的发现哦!

    const int Repetitions = 100;
    static int index_thread = 0;
    static int index_main = 0;
    static void Main(string[] args)
    {
        ThreadStart threadStart = DoWork;
        Thread thread = new Thread(threadStart);
        thread.Start();
        //Main线程启动一个循环
        for (int count = 0; count < Repetitions; count++)
        {
            index_main++;
            Console.Write('-');
        }
        Console.WriteLine($"
    index_thread:{index_thread}");
        Console.WriteLine($"index_main:{index_main}");
        Console.WriteLine("(主线程最后一个语句...)");
    }
    static void DoWork()
    {
        for (int count = 0; count < Repetitions; count++)
        {
            index_thread++;
            Console.Write("+");
            if (count == Repetitions - 1)
            {
                Debug.Write("我创建的线程执行完成了.....................................
    ");
            }
        }
    }

    假如你是直接按F5,可以在Visual Studio输出栏看到

    结论:

    1.操作系统在所有前台线程(主线程和新创建的线程都是前台线程)结束后终止进程,虽然在控制台中输出的index_thread不总是100

    2.主线程以外的线程执行情况是不确定的,

    3.实际上,主线程会等待所有子线程(前台线程)结束后,结束主线程,关闭进程,结束程序

    4.由于子主线程执行情况的不确定性,在主线程输出index_thread的时候,可能子线程循环结束了,也可能没结束,所以导致结果总是不为100

    通过Join方法阻塞主线程,等待子线程执行结束

    //省略部分代码
    thread.Join();
    Console.WriteLine($"
    index_thread:{index_thread}");

    这样,就可以保证在此之后,子线程已经运行结束了,每次输出的结果都为100

    使用线程池

    const int Repetitions = 1000;
    static int index_thread = 0;
    static int index_main = 0;
    static void Main(string[] args)
    {
        WaitCallback waitCallBack = DoWork;
        ThreadPool.QueueUserWorkItem(waitCallBack, '+');
        //Main线程启动一个循环
        for (int count = 0; count < Repetitions; count++)
        {
            index_main++;
            Console.Write('-');
        }
        Console.WriteLine($"
    index_thread:{index_thread}");
        Console.WriteLine($"index_main:{index_main}");
        Console.WriteLine("主线程最后一个语句");
    }
    private static void DoWork(object ch)
    {
        for (int count = 0; count < Repetitions; count++)
        {
            index_thread++;
            Console.Write(ch);
            if (count == Repetitions - 1)
            {
                Debug.Write("我创建的线程执行完成了.....................................
    ");
            }
        }
    }

    优点:

    1.解决线程太多造成的性能方面的负面影响

    2.高效的利用处理器

    2.结合lambda使用委托,代码可以更精简

    注意点

    1.使用线程池创建的线程都是后台线程

    2.不要使用线程池运行时间特别长的任务,尽量不要I/O受限

    异步任务

    static void Main(string[]  args)
    {
        Task task = Task.Run(()
            =>
        {
            var t = Thread.CurrentThread;
            for (int count = 0; count < Repetitions; count++)
            {
                index_thread++;
                Console.Write('+');
            }
        });
        for (int count = 0; count < Repetitions; count++)
        {
            index_main++;
            Console.Write('-');
        }
        //类似THread.Join方法
        task.Wait();
        Console.WriteLine($"
    index_thread:{index_thread}");
        Console.WriteLine($"index_main:{index_main}");
        Console.WriteLine("Over");
        Console.ReadLine();
    }

    Task是.Net Framwwork4引入的一个类库,它在使用上比Thread简单了,可控性又THreadPool强了,默认情况下,它也是从线程池中请求一个线程来执行任务.

    与ThreadPool相同的是,当创建时(调用Run)启动,与Thread相同的是,可以通过Wait()方法阻塞上下文线程(主线程)等待任务执行完成

    带返回值的异步任务

    static void Main(string[] args)
    {
        Task<string> task = Task.Run(()
            => "string 类型 返回值");
        for (int i = 0; i < 1000; i++)
        {
            if (task.IsCompleted)
            {
                Console.Write('任务完成了');
                break;
            }
            Console.Write('.');
        }
        Console.WriteLine(task.Result);
    }

    泛型的Task表示该任务具有返回值,IsCompleted表示任务是否完成

    要注意的是,调用Result属性的时候,会阻塞上下文进程(内部执行Wait())

  • 相关阅读:
    CentOS重置Mysql密码
    2017年2月21日20:35:46
    UEFI+GPT安装windows
    CentOS 7.0 使用 yum 安装 MariaDB 与 MariaDB 的简单配置
    CentOS利用nginx和php-fpm搭建owncloud私有云
    Docker安装CentOS
    CoreOS和Docker入门
    Docker命令学习
    CentOS安装Redis详细教程
    Redis的三种启动方式
  • 原文地址:https://www.cnblogs.com/cheesebar/p/6549656.html
Copyright © 2011-2022 走看看