C#面试记录

问题总结

1. 多线程和多进程的区别;

下面是我在知乎-pansz上转载的一个答案,非常通俗地回答了这个问题。

单进程单线程:一个人在一个桌子上吃菜。 单进程多线程:多个人在同一个桌子上一起吃菜。 多进程单线程:多个人每个人在自己的桌子上吃菜。

多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。
1)对于 Windows 系统来说,【开桌子】的开销很大,因此 Windows 鼓励大家在一个桌子上吃菜。因此 Windows多线程学习重点是要大量面对资源争抢与同步方面的问题。
2)对于 Linux 系统来说,【开桌子】的开销很小,因此 Linux鼓励大家尽量每个人都开自己的桌子吃菜。这带来新的问题是:坐在两张不同的桌子上,说话不方便。因此,Linux下的学习重点大家要学习进程间通讯的方法。 开桌子的意思是指创建进程。开销这里主要指的是时间开销。
可以做个实验:创建一个进程,在进程中往内存写若干数据,然后读出该数据,然后退出。此过程重复 1000 次,相当于创建/销毁进程 1000 次。在我机器上的测试结果是: UbuntuLinux:耗时 0.8 秒 Windows7:耗时 79.8 秒 两者开销大约相差一百倍。
这意味着,在 Windows 中,进程创建的开销不容忽视。换句话说就是,Windows
编程中不建议你创建进程,如果你的程序架构需要大量创建进程,那么最好是切换到 Linux 系统。 大量创建进程的典型例子有两个,一个是 gnuautotools 工具链,用于编译很多开源代码的,他们在 Windows 下编译速度会很慢,因此软件开发人员最好是避免使用Windows。
另一个是服务器,某些服务器框架依靠大量创建进程来干活,甚至是对每个用户请求就创建一个进程,这些服务器在 Windows下运行的效率就会很差。这"可能"也是放眼全世界范围,Linux 服务器远远多于 Windows 服务器的原因。

2. 多线程如何使用;

前台进程VS后台进程:
  前台线程(用户界面线程) 只要存在有一个前台线程在运行,应用程序就在运行 通常用来处理用户的输入并响应各种事件和消息 后台线程(工作线程)
  应用程序关闭时,如果后台线程没有执行完,会被强制性的关闭 用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等

C#多线程的5种写法:
参考:https://blog.csdn.net/qq_44705559/article/details/117382445
https://zhuanlan.zhihu.com/p/685335161
1)线程类——Thread Thread类创建的线程默认为前台线程,可以通过IsBackground属性设置其为前台或后台线程。
2)线程池——ThreadPool 线程池其实就是一个存放线程对象的“池子(pool)”,他提供了一些基本方法,
如:设置pool中最小/最大线程数量、把要执行的方法排入队列等等。
ThreadPool是一个静态类,因此可以直接使用,不用创建对象。
每新建一个线程都需要占用内存空间和其他资源而新建了那么多线程,有很多在休眠,或者在等待资源释放;
又有许多线程只是周期性的做一些小工作,如刷新数据等等,太浪费了,划不来。实际编程中大量线程突发,然后在短时间内结束的情况很少见。
于是,就提出了线程池的概念。线程池中的线程执行完指定的方法后并不会自动消除,而是以挂起状态返回线程池,如果应用程序再次向线程池发出请求,那么处以挂起状态的线程就会被激活并执行任务,而不会创建新线程,这就节约了很多开销。只有当线程数达到最大线程数量,系统才会自动销毁线程。因此,使用线程池可以避免大量的创建和销毁的开支,具有更好的性能和稳定性,其次,开发人员把线程交给系统管理,可以集中精力处理其他任务。
3)任务——Task 相比于Thread类,Task类为控制线程提供了更大的灵活性。 Task类可以获取线程的返回值
可以定义连续的任务:在一个任务结束结束后开启下一个任务
可以在层次结构中安排任务,在父任务中可以创建子任务这样就创建了一种依赖关系,如果父任务被取消,子任务也随之取消。
4)BackgroundWorker组件
该控件提供了DoWork, ProgressChanged 和 RunWorkerCompleted事件
为DoWork添加事件处理函数,再调用RunWorkerAsync()方法,即可创建一个新的线程。
执行DoWork任务ProgressChanged和RunWorkerCompleted事件均在UI线程中执行。
添加相应的处理函数,即可完成任务线程与UI线程间的交互,可用于显示任务的执行状态(完成百分比)、执行结果等。
同时,该控件还提供了CancleAsync()方法,以中断线程的执行
5)Parallel类——Parallel
Parallel和Task类都位于System.Threading.Task命名空间中,是对Thread和ThreadPool类更高级的抽象。
Parrallel类有For()、ForEach()、Invoke()三个方法
Invoke()——实现任务并行性,允许同时调用不同的方法。
Parallel.For()和 Parallel.ForEach()——实现数据并行性,在每次迭代中调用相同的代码

  初步总结:

线程VS线程池
1、任务是架构在线程之上的,任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系
==》比如开10个任务并不是说会开10个线程,一个任务可能包含几个线程(类似线程池)
3、Task和Thread一样,位于System.Threading命名空间下
线程VS任务
1、任务相比线程池有很小的开销和精确的控制。

 那么,如何选择用哪种方式创建多线程呢?

  • C#官方不建议直接使用Thread和线程池,每创建一条线程,都占用CPU时间,再者有些业务本身就不适合多线程,比喻说现在的CPU本身就尝试优化指令,分配到多个线程效率更低了。
  • C#的开发者,在考虑充分利用这门语言的特性的背景下,花费了很大资源来优化Task和Parallel,一般情况下Task就够了
  • sleep(0)效果相当于yield(),会让当前线程放弃剩余时间片,进入相同优先级线程队列的队尾,只有排在前面的所有同优先级线程完成调度后,它才能再次获执行的机会。
  • 更推荐使用Task来实现异步
  • 线程池是为突然大量爆发的线程设计的
  • Task是基于线程池封装实现的,解决了线程池无法挂起中止线程等这些问题。

3. 系统假死问题怎么解决;

1)出现的原因:
一种是在UI线程上调用耗时较长的操作,例如访问数据库,这种阻塞是UI线程被占用所导致,可以通过delegate.BeginInvoke的异步编程解决;
另一种是窗体加载大批量数据,例如向ListView、DataGridView等控件中添加大量的数据。
2)解决方法:
用委托,不要跨UI使用线程
某个函数耗时太久。在遇见这种情况的时候,我们就可以考虑使用async + await + Task来解决
减少使用此语句:主线程休眠Thread.Sleep(3000);
使用下列语句替代:

	public static void Delay(int mm)
	     {
	         DateTime current = DateTime.Now;
	         while (current.AddMilliseconds(mm) > DateTime.Now)
	         {
	             Application.DoEvents();
	         }
	         return;
	     }

4. RS232和RS485区别;

- RS232优点:成本低,设计简单(支持多种设备),无噪音	
	   缺点:传输速度不如RS485快;
- RS485优点:传输快,抗噪性强,支持从机和单主机
	   缺点:一次只能有一个节点传输数据

RS232和RS485的区别

5. Modbus TCP/IP的使用;

Modbus 通信协议特点(RTU、ASCII、TCP)

 Modbus 协议定义了一个控制器能够认识使用的消息结构,而不管它们是经过何种网络进行通信的。因此,底层通信方式可以使用 RS232,RS485 等串行链路,也可以使用 TCP/IP 以太网链路。当在 Modbus 网络上通信时,控制器必须要知道该网络中其他从设备的地址,才能识别按地址发来的消息,并作出相应行为。

  • 简单来说,Modbus 通信协议具有以下几个特点:
    • Modbus 协议标准开放、公开发布且无版税要求,用户可以免费获取并使用 Modbus 协议,不需要缴纳许可证费;
    • Modbus 最开始使用 RS232,RS485 等串行链路作为底层通信方式,串行总线的接口芯片成本低,而且布线也简单方便;
    • Modbus 协议支持多种电气接口,如 RS232、RS485、TCP/IP 等,还可以在各种介质上传输,如双绞线、光纤、红外、无线等;
    • Modbus 是简单地应用层协议,其协议消息帧格式简单、紧凑、通俗易懂,便于用户理解和使用、厂商开发和集成,方便形成工业控制网络。
      Modbus开发专业术语
      通讯过程:
      Modbus通讯过程(主从方式)
      有关Modbus详细内容参考:
      Modbus通讯协议详解1
      Modbus通讯协议详解2

6. Socket的特点;

socket:
  这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。
  socket只是一种连接模式,不是协议,socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)
  很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,
这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的
Socket所处层级

  • Socket 传输的特点:
    • 优点
      1)传输数据为字节级,传输数据可自定义,数据量小(对于手机应用讲:费用低)
      2)传输数据时间短,性能高
      3)适合于客户端和服务器端之间信息实时交互
      4)可以加密,数据安全性强
    • 缺点:
      1)需对传输的数据进行解析,转化成应用级的数据
      2)对开发人员的开发水平要求高
      3)相对于Http协议传输,增加了开发量

7. 引用类型和值类型;

值类型
  也就是基本数据类型 基本数据类型常被称为四类八种。

  • 四类八种:
    • 整型(4种):byte(1 byte)、short(2 byte)、int(4 byte)、long(8 byte)
    • 浮点型(2种):float(4 byte)、double(8 byte)
    • 字符型(1种):char(2 byte)
    • 逻辑型(1种):boolean
  • 其他所有的类型都称为引用类型(数组、类、接口、字符串等)
PS:值传递VS引用传递
  • 值传递
    基本数据类型赋值都属于值传递,值传递传递的是实实在在的变量值,是传递原参数的拷贝,值传递后,实参传递给形参的值,形参发生改变而不影响实参。
  • 引用传递
    引用类型之间赋值属于引用传递。引用传递传递的是对象的引用地址,也就是它的本身(自己最通俗的理解)。引用传递:传的是地址,就是将实参的地址传递给形参,形参改变了,实参当然被改变了,因为他们指向相同的地址。
    值类型VS引用类型

8. 委托怎么使用?和指针的关系。

1)委托是一种类型
2)委托是一种类型与返回值相同函数的链表
3)委托使用+=,=,-=三个符号操作链表里的函数
4)调用委托就会依次调用委托链表里的所有函数
5)委托返回值默认是最后一个添加的函数返回值

委托举例

补充:事件委托

委托就像是一串鞭炮。将函数链接与取消或者触发,可以自由使用。但是事件限定了只能由声明事件的类来触发。而链接函数的过程,可以看做一种通知。

using System;
using System.Data;
using System.IO;
namespace ConsoleApp3
{
    delegate void func(string name);
    class Program
    {
        
        static void Main(string[] args)
        {
            Person Hei1 = new Person("黑人小哥1");
            Person Hei2 = new Person("黑人小哥2");
            Person Hei3 = new Person("黑人小哥3");
            Person Hei4 = new Person("黑人小哥4");
            Person ming = new Person("小明");
            ming.died += Hei1.taiGuan;
            ming.died += Hei2.taiGuan;
            ming.died += Hei3.taiGuan;
            ming.died += Hei4.taiGuan;
            ming.fellFull += Hei1.seeFull;
            ming.fellFull += Hei2.seeFull;
            ming.fellFull += Hei3.seeFull;
            ming.fellFull += Hei4.seeFull;
            ming.fellHungry += Hei1.seeHungry;
            ming.fellHungry += Hei2.seeHungry;
            ming.fellHungry += Hei3.seeHungry;
            ming.fellHungry += Hei4.seeHungry;
            int i;

            try {
                while (true){
                    Console.WriteLine("请输入小明吃甜甜圈的数量:");
                    i = int.Parse(Console.ReadLine());
                    ming.EatDonut(i);
                    Console.ReadKey();
                    Console.Clear();
                }
            }
            catch (Exception e) { 
            
            }
           
        }
    }
    class Person{
        string name;
        public event func died;
        public event func fellFull;
        public event func fellHungry;
        public Person(string name) {
            this.name = name;
        }
        
        public void EatDonut(int i){
            if (i <= 10)
            {
               fellHungry(name);
            }
            else {
                if (i <= 100)
                {
                    fellFull(name);
                }
                else {
                    if (i > 100) {
                        died(name);
                    }
                }
            }
        }
        public void taiGuan(string name) {
            Console.WriteLine(this.name+"收到"+name+"死了,开始抬棺!奏乐!!");
        }
        public void seeHungry(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "还很饿");
        }
        public void seeFull(string name)
        {
            Console.WriteLine(this.name + "看到" + name + "吃饱了");
        }
    }
}

  利用+=注册事件以后,+=后面的函数何时触发,就由事件声明的主体来控制。因为我们都想知道这件事情发生以后,我们要预期什么效果。而声明类之外的函数,我们只能选择接受"+=“或者拒绝接收”-=“,我们无法清空”=",或者调用该事件。

9. 没有遇到高并发问题?如何解决?

  字面意思就是“同时做多件事”。首先现在高并发的解决方案都非常成熟了,不仅是Java能做好,Pyton和C#也是毫无问题的

  • 异步编程
    • 通过使用async/await关键字,可以像写同步代码那样编写异步代码,所有的回调和事件处理都交给编译器和运行时帮你处理了,简单好用。
      用异步编程有两个好处:不阻塞主线程(比如UI线程),提高服务端应用的吞吐量。所以微软推荐ASP.NET中默认使用异步来处理请求。
  • 并行编程
    • 提高CPU的利用率,更适合客户端的一些应用,对于服务端的应用可能会造成负面影响。
    • 通过使用并行处理库,你不用关心Task的创建和管理(当然更不用说底层的线程了),只需要关注处理任务本身就行了。主要靠.NET 4.0引入的任务并行库和并行LINQ。
  • 响应式编程
  • 数据流编程

10. 工作中遇到的最大的问题,是如何解决的?

  • 问题:
    • 客户提供的协议不全or不提供协议,需要自己去抓取协议,并作出解析。
  • 解决:
    • 使用串口工具AccessPort137工具,监听串口,抓取上位机发送的指令,并将其导出,进行解析。
    • 再次使用串口工具,发送抓取的指令,确认上一步的操作是否正确。推荐的工具:XCOM、UartAssist或者自己写一个(以485通讯为例)

PS:

  1. ODM:自研;OEM:代工
  2. 3C行业:3C产业是指结合电脑、通讯和消费类电子科技产品整合应用的资讯家电产业,出现于90年代后期,伴随着半导体及网络的普及而出现,渐渐发展成为世界性的新兴科技产业。

3C是指,Computer,Communication ,Consumer Electronic

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值