c# hdf5 写string_C#.NET Thread多线程并发编程学习与常见面试题解析-1、Thread使用与控制基础...

b6083efbac836b6d7c13ad6629527572.png
前言:
因为平时挺少用到多线程的,写游戏时都在用协程,至于协程那是另一个话题了,除了第一次学习多线程时和以前某个小项目有过就挺少有接触了,最近准备面试又怕被问的深入,所以就赶紧补补多线程基础。
网上已经有很多线程编程的学习笔记了,那我为什么还要再整理一篇呢。因为我在搜索网上文章的时候发现一般别人整理的面试文章那很多语法都一笔带过了默认大家都懂,学习文章又很少有给出经典的题目,一般都是要几篇集合着一起看,既然如此的话我为什么不自己整理出一份呢?自己看的轻松,说不定以后也有人喜欢这种风格能帮助到别人。所以这篇文章也会参考很多其他的文章,最后都会写上引用的。
我写算法的时候也是很喜欢用C++ 来学习,而且笔试的时候我或者很多公司也喜欢用C++,毕竟C++ 的控制台程序输入输出格式化做的也比较好,那为什么这篇又用的是C#而不是C++ 呢?因为最近实习只带着笔记本,我的笔记本上只装了vscode用来写轻量级程序学习。C++装的是MinGW来编译,但是MinGW因为跨平台吧对std::thread支持的又不太好,反正又不是不会用别的语言,最重要的是学习的思想嘛。

一、进程和线程有什么区别?为什么要使用多线程?

首先用最经典的一道面试题作为引入。
①进程是资源分配的最小单位,线程是CPU调度的最小单位。
②一个线程只能属于一个进程,而一个进程可以有多个线程。
③进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
④进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉。
⑤进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。
⑥部分任务可能比较耗时,长时间占用CPU(你肯定不希望应用执行某个功能时整个程序都卡死),如果创建进程解决可能额外CPU开销更大,因此部分时候需要使用多线程技术。

430b4945414890cee35a186778d07e3a.png

二、C# 中使用多线程

在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。 当 C# 程序开始执行时,主线程自动创建使用 Thread 类创建的线程被主线程的子线程调用。
再介绍Thread类中比较有用的一个静态方法,Sleep,用于挂起(可以看成暂停)线程一段时间,参数是毫秒。
下面来一个简单的例子看Thread的使用:

using System;
using System.Threading;
namespace LeeCarry
{
    public class Test
    {
        public static void Main(string[] args)
        {
            Thread.Sleep(1000);//主线程暂停
            Thread npt=new Thread(NonParaThread);
            npt.Start();
            Thread wpt=new Thread(WithParaThread);
            wpt.Start("我是带参数的线程。");
        }

        private static void NonParaThread()
        {
            Console.WriteLine("我是不带参数的线程。");
            Thread.Sleep(1000);//子线程暂停
            Console.WriteLine("我是不带参数的线程。");
        }

        private static void WithParaThread(Object obj)
        {
            Console.WriteLine(obj.ToString());
        }
    }
}

执行该程序首先暂停1秒(1000毫秒),接着连续输出 我是不带参数的线程我是带参数的线程
然后再暂停1秒,接着输出我是不带参数的线程
一开始是在主线程中挂起(暂停)的,所以两个线程都要等1秒才执行,后面是在不带参数的线程中挂起的,不影响另一个线程,所以带参数的线程就直接输出了。
但是要注意的是我这里是在Thread直接指定了方法,但实际上该方法有委托类型

public delegate void ThreadStart()//用于无参数方法
public delegate void ParameterizedThreadStart(object obj) //用于有参数方法

所以说刚刚的实例化可以展开成如下形式

Thread npt=new Thread(new ThreadStart(NonParaThread));
Thread wpt=new Thread(new ParameterizedThreadStart(WithParaThread));

这里的参数用的是object,有可能会被问到一个问题就是拆箱装箱,当然拆箱装箱和之前提的委托,包括什么匿名函数、lambda之类的都是另一个话题了,这里为了保持知识的独立性不过多的引入其他特性了,只在必要的时候讲。

三、资源抢占与信号

上面的内容十分简单,似乎跟普通的实例化类调用下函数没什么区别啊。
那我们再用一道面试题作为引入:

两个线程交替打印0~100的奇偶

如果只用上面的知识写出下面的代码,那运行一下就可以发现问题所在了。

using System;
using System.Threading;
namespace LeeCarry
{
    public class Test
    {
        public static void Main(string[] args)
        {
            Thread oddThread=new Thread(OddThread);
            Thread evenThread=new Thread(EvenThread);
            evenThread.Start(); 
            oddThread.Start();

        }

        private static void EvenThread()
        {
            for(int i=0;i<=100;i+=2)
            {
                Console.WriteLine("线程1:{0}",i);
            }
        }

        private static void OddThread()
        {
            for(int i=1;i<=100;i+=2)
            {
                Console.WriteLine("线程2:{0}",i);
            }
        }
    }
}

结果:

95af9a9987ae37e069baea1c0182f868.png

当然这个结果不一定是一样的,毕竟是两个线程并发在跑,但是却只有一个控制台啊,当然,往深一点说就是多个线程共享的资源。
就像十字路口如果不控制车辆开动的顺序,仍由他们乱开会引发严重的后果一样,实际上我们在编程中也经常会遇到控制线程顺序的需求。

01118da574b73b4d92a8ac2844889bd6.gif

那说到这其实也很好理解了,就跟十字路口需要红绿灯一样,我们也会用到信号灯的思想去控制线程的执行顺序。
在C#中有封装好类EventWaitHandle(并不严谨,下期说,这期是为了方便理解概念),有几个成员方法,
WaitOne():如果是红灯的话会将线程暂停在当前位置。
Set():相当于开绿灯,允许被暂停的线程通过开始执行下面的代码了
Reset():相当于开红灯,线程遇到wait会暂停住。

EventWaitHandle必须指定一个枚举类型,AutoReset或ManualReset,ManualReset很好理解,就是手动开关红绿灯。而AutoReset是指在执行Set()后会马上自定执行一次Reset(),相当于只是把当前在红灯前的线程放行。
那么有了这些知识我们就可以开始写代码了

using System;
using System.Threading;
namespace LeeCarry
{
    public class Test
    {
        public static EventWaitHandle oddFlag=new EventWaitHandle(false,EventResetMode.AutoReset);
        public static EventWaitHandle evenFlag=new EventWaitHandle(false,EventResetMode.AutoReset);

        public static void Main(string[] args)
        {
            Thread oddThread=new Thread(OddThread);
            Thread evenThread=new Thread(EvenThread);
            evenThread.Start(); 
            oddThread.Start();

        }

        private static void EvenThread()
        {
            for(int i=0;i<=100;i+=2)
            {
                Console.WriteLine("线程1:{0}",i);
                oddFlag.Set();
                evenFlag.WaitOne();
            }
            oddFlag.Set();//最后开一次绿灯防止线程一直被阻塞,也可以在waitone加时间参数
        }

        private static void OddThread()
        {
            oddFlag.WaitOne();//确保偶数线程先运行
            for(int i=1;i<=100;i+=2)
            {
                Console.WriteLine("线程2:{0}",i);
                evenFlag.Set();
                oddFlag.WaitOne();
            }
            evenFlag.Set();//最后开一次绿灯防止线程一直被阻塞,也可以在waitone加时间参数
        }
    }
}

当然,这里写成开了两个flag完全是为了方便理解,事实上可以只开一个叫flag,然后把evenFlag和oddFlag都改成flag,毕竟只有两个线程,要么停要么走嘛。
最后我们似乎能把两个函数再合成成为一个函数,但是这样的话单单一个信号灯似乎是没办法解决的了,可以像上面用两个信号量或者下篇还有机会出来的话讲讲锁。


引用:

.NET面试题解析(07)-多线程编程与线程同步-/梦里花落知多少/

Thread Class-MSDN

C#多线程-菜鸟教程

EventWaitHandle Class-MSDN

多线程C#面试题-Ax0ne

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2020/5月/15好上传最新版 JavaGuide 目前已经 70k+ Star ,目前已经是所有 Java 类别项目中 Star 数量第二的开源项目了。Star虽然很多,但是价值远远比不上 Dubbo 这些开源项目,希望以后可以多出现一些这样的国产开源项目。国产开源项目!加油!奥利给! 随着越来越多的人参与完善这个项目,这个专注 “Java知识总结+面试指南 ” 项目的知识体系和内容的不断完善。JavaGuide 目前包括下面这两部分内容: Java 核心知识总结; 面试方向:面试题、面试经验、备战面试系列文章以及面试真实体验系列文章 内容的庞大让JavaGuide 显的有一点臃肿。所以,我决定将专门为 Java 面试所的文章以及来自读者投稿的文章整理成 《JavaGuide面试突击版》 系列,同时也为了更加方便大家阅读查阅。起这个名字也犹豫了很久,大家如果有更好的名字的话也可以向我建议。暂时的定位是将其作为 PDF 电子书,并不会像 JavaGuide 提供在线阅读版本。我之前也免费分享过PDF 版本的《Java面试突击》,期间一共更新了 3 个版本,但是由于后面难以同步和订正所以就没有再更新。《JavaGuide面试突击版》 pdf 版由于我工作流程的转变可以有效避免这个问题。 另外,这段时间,向我提这个建议的读者也不是一个两个,我自己当然也有这个感觉。只是自己一直没有抽出时间去做罢了!毕竟这算是一个比较耗费时间的工程。加油!奥利给! 这件事情具体耗费时间的地方是内容的排版优化(为了方便导出PDF生成目录),导出 PDF 我是通过 Typora 来做的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值