博客文章

从 2019 年至今的文章,共 32 篇。

按时间浏览

谁依赖谁:用换设备与做测试理解 DIP、IoC 和 DI

一段控制设备的代码已经能正常工作。现在,要把灯换成风扇,应该改哪里?如果手边没有真实设备,能不能先验证控制逻辑?这两个问题,能帮我们看清代码中的依赖关系。DIP、IoC、DI 这几个经常一起出现的概念,也可以沿着同一次修改来理解。本文先把一个开关逐步改到可以换设备、做测试,再把这个思路放到扫描系统和 C 语言的算法选择中。示例刻意简化了设备操作,重点看...

WPF 中的死锁

桌面应用程序的一个关键点:不要堵塞 UI 线程。与 UI 进行任何交互,比如单击按钮,都从 UI 线程开始执行事件。当你在 UI 线程上做事情的时候,你的窗口会完全冻结。因此,我们通常会在另一个线程执行某些操作,就不会冻结。然而,在另一个线程上的操作完成后,我们可能需要更改 UI。任何 UI 的更改都应该在 UI 线程上进行。这意味着我们需要回到 UI...

C# 死锁--嵌套锁

本文将介绍如何理解死锁,死锁的常见类型,如何解决死锁,如何调试死锁,以及避免死锁的最佳实践。什么是死锁C# 中的死锁是指两个或多个线程在执行过程中被冻结,因为它们正在等待彼此完成。例如,线程 A 正在等待由线程 B 持有的 locker2,线程 B 不能完成并释放 locker2,因为它正在等待由线程 A 持有的 locker1。例如死锁案例 – 嵌套...

使用 Monitor 类实现生产者-消费者模式

如果你仔细看了 Monitor 类的 API,除了用于同步访问共享数据的 Monitor.Enter 和 Monitor.Exit 方法外,还有 Monitor.Wait,Monitor.Pulse 和 Monitor.PulseAll 方法,它们可以用于线程间通信。生产者-消费者的简单实现Monitor.Wait 和 Monitor.Pulse 方法...

使用 lock 语句的最佳实践

在多线程 C# 应用程序中同步访问共享资源时,通常使用 lock 语句 – 部分原因是 lock 语法简单。但是,C# 开发人员在使用 lock 语句时,很容易不考虑它的功能和需求,其实 lock 语句的基本用法也是有一些陷阱的。锁对象是引用类型,而不是值类型刚开始你可能会写出如下代码:private static Int32 locker;publi...

Monitor 和 Lock 同步访问共享资源

为了理解同步访问共享资源,我们来看一个例子:2 个线程向 Dictionary 添加数据。internal class Program{ private static Int32 PRODUCT_NUMBER = 100; private static Dictionary<Int32, String> dics = new D...

UART(Universal Asynchronous Receiver Transmitter)通信

在使用串口调试助手或者进行串口编程时,往往要选择串口号,以及设置波特率,校验位,数据位,停止位才能正常通信。数据帧格式数据帧格式是通信协议所必须的,它帮助设备之间正确理解彼此之间的谈话内容。下图便是 UART 通信的数据帧格式:UART 数据帧格式包含了起始位,数据,奇偶校验位,还有停止位。传输数据之前,UART 的发送线和接收线是高电平,表明线路是空...

向量(Vector)

对于向量这个概念,很多人都是既熟悉又陌生。可能是因为不同学科对向量的定义和表示存在一定的区别,造成大家的理解困难。向量是什么我们从数学,物理,计算机三个角度来看待如何定义⌈向量⌋。物理专业角度在物理学科中,一般将向量称为⌈矢量⌋,并且与⌈标量⌋一词相对。向量是空间中的箭头。决定一个向量的是:它的长度和它所指的方向。计算机专业角度向量是有序的数字列表。向...

UDP 报文数据包长度

不知道你在使用 C# 进行 UDP 网络编程时,是否遇到过下面的错误:运行下面的代码会复现上面的错误信息:try{ Byte[] datas = new Byte[UInt16.MaxValue]; var client = new Socket(SocketType.Dgram, ProtocolType.Udp); client....

MathJax 数学公式

MathJax 向量$$\overrightarrow{v} = \left( \begin{array}{c} 1 \\\\ 2 \end{array} \right)$$\[\overrightarrow{v} = \left( \begin{array}{c} 1 \\\\ 2 \end{array} \right)\]方括号矩阵$$A =\be...

C 语言如何实现泛型

C 语言是过程式编程语言,C 语言设计的目标是提供一种能以简易的方式编译、处理底层内存产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。而对于更高阶、更抽象的编程范式来说,C 语言这种基于过程和底层的设计初衷就会成为它的短板。因为,在编程世界中,更多的编程工作是解决业务上的问题,而不是计算机的问题,所以,我们需要更为贴近业务、更为抽象的语言...

在 Visual Studio 中配置 C/C++ 项目的最佳实践

今天,我将谈论在 Visual Studio 中,配置项目的最佳实践。这是我的最佳实践,可能不是你的最佳实践。创建一个 Calculator 项目尝试点一下 “Solution Explorer” 下的 “Show All Files”:添加项目文件在 Calculator 项目下,增加一个 “src” 文件夹,然后添加一个 main.cpp 文件:设...

C/C++ 编译器 - 链接

.cpp 文件编译成 obj 文件后,还需要经过一个称为“链接”的过程。链接的主要内容就是找到每个符号和函数的位置以及将每个单独的 obj 文件链接在一起,生成可执行文件。单个 obj 文件没有任何关系,这些文件不能交互,所以,如果我们决定把程序分割成多个文件,我们需要一种方法把这些文件链接起来形成一个程序,这就是链接器的作用。main 函数在我们的项...

C/C++ 编译器 - 编译

编译阶段,根据目标处理器的架构,编译器会将预处理器生成的代码转化为对应的汇编指令。汇编阶段,汇编器会把汇编指令转化为机器码或者目标文件编译和汇编回到我们的项目,恢复项目属性页的设置如下:代码也恢复到如下内容:int add(int x, int y){ int result = x + y; return result;}按 Ctrl + ...

C/C++ 编译器 - 预处理

预处理器是在程序源文件被编译之前根据预处理指令对程序源文件进行处理的程序。预处理器指令以 # 号开头标识,末尾不包含分号。预处理命令不是C/C++语言本身的组成部分,不能直接对它们进行编译和链接。C/C++提供的预处理功能主要有文件包含(#include)、宏替换(#define)、条件编译(if, #ifdef)等。头文件包含#include 实际上...

C# 应用程序调用 MATLAB 创建的 .NET 程序集

此示例演示如何从 MATLAB 函数生成 .NET 程序集,并将生成的程序集集成到 .NET 应用程序中。在 MATLAB 中创建函数第一步:在 MATLAB 中,分别创建名为 init, add, sub, mul, div 的 MATLAB 脚本。第二步:在 Command Window 中输入 deploytool 并按 Enter 键进入 MA...

TI FMCW 雷达 - 测距

Hello, everybody, and welcome to the series of technical videos on Millimeter Wave Sensing. Specifically, we’ll be talking about a sensing technology called FMCW RADARS, which i...

Position, LookDirection and UpDirection of 3D Camera

想象一下,你在 3D 空间中移动你的头而不是 Camera。然后,Camera.Position是指定你的头部位置,Camera.LookDirection 决定你看的方向,而 Camera.UpDirection 表示你头部的方向。下面的图片为你澄清一些事情。在第一张图片中,相机以 + Z 轴为中心,向下看到 -Z 轴和你的形状:当你设置照相机的位置...

深入理解 SynchronizationContext

在开发 Windows 窗体应用程序时,关于 UI 线程,大多数开发者都知道的两条黄金法则: 不要在 UI 线程上执行任何耗时的操作。 不要在非 UI 线程上访问任何 UI 控件。那么问题来了,如果我正在编写一个需要在线程池线程执行一部分工作,然后在 UI 线程上再进行一部分工作的组件,我们应该如何做呢?WPF 有一个 DispatcherSync...

如何动态隐藏/显示 WPF 应用程序的控制台窗口

你曾经是否想过在 WPF 应用程序运行起来后,可以动态打开/关闭控制台窗体。也就是说,你可以在 WPF 应用程序里,留一个后门,用于调试程序。在需要时,打开控制台窗口,查看后台监控数据;不需要时,关闭控制台窗口。下面的方法应该可以满足你的需求: 将 WPF 应用程序输出类型设置为控制台应用程序,在 *.csproj 文件里可以看到如下配置。<O...

NULL OBJECT 模式

考虑如下代码:Employee e = DB.GetEmployedd("Bob");if (e != null && e.IsTimeToPay()){ e.Pay();}上面代码的意思是:从数据库中获取名为 “Bob” 的 Employee 对象。如果该对象不存在,DB 对象就返回 null;否则,就返回请求的 Employe...

P/Invoke,C# 与 C++ 交互技术

P/Invoke『Platform Invoke』是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。本博文教程源码 https://gitee.com/luli100/pinvoke-tutorial。结构体的封装和调用方式默认情况下,非托管结构与托管结构在内存中的布局不同,因此,成功跨托管/非托管边界传递结构需要额外的步骤来保留数据完整性...

在 WPF 中使用 MVVM 模式时,如何打开子窗口

通常来说,MVVM 模式中的 ViewModel 是不需知道 View 是如何设计的。也就是说,从 ViewModel 中访问『创建』View 是错误的设计。直接在 View 后台代码打开子窗口public partial class MainWindow : Window{ private readonly MainViewModel Main...

MVVM 模式在 WPF 中的正确使用方式

我想使用过 WPF 的程序员,应该都知道 MVVM 模式。然而,争议最多的是,是否应该在 Model 类中实现 INotifyPropertyChanged 接口。我的结论是:不应该在 Model 类中实现 INotifyPropertyChanged 接口。MVVM 层与层的依赖关系Model 代表真实状态内容的领域模型(面向对象),或者代表数据访问...

缓存更新之 Cache Aside Pattern

Cache Aside 模式可能是最常用的缓存更新方法。使用这种方法,你的应用程序直接与缓存和数据库打交道,也就说,你的代码负责协调缓存和真实数据源之间的数据流向。读数据读数据的具体逻辑如下: 应用程序首先检查缓存。 如果在缓存中找到数据『缓存命中』,数据被读取并返回给客户端。 如果在缓存中未找到数据『缓存未命中』,应用程序查询数据库以读取数据,...

C# 跨机器大端小端字节数组转为结构体对象

通常,在我们接收到网络字节数组后,会将字节数组转换为对应的结构体对象。忽略大小端问题的实现方式在 C# 语言中,下面代码是最优雅的,性能也能得到保证的实现方式。using System.Runtime.InteropServices;namespace EndianExtensionsTests{ [StructLayout(LayoutKind...

如何创建 WPF 单实例应用程序

你曾经是否遇到过只准打开一个 WPF 应用程序实例这样的需求,如果有这样的需求场景,下面的方法应该可以满足你的需求:public partial class App : Application{ private EventWaitHandle AppEventWait; protected override void OnStartup(S...

Task.Run 的正确使用方式(4)

在 WPF 事件处理器中使用 Task.Run 的方式,是否适用于 ASP.NET MVC 控制器呢,答案是否定的。ASP.NET MVC 控制器更喜欢 CPU-bound 的同步方法class MyService{ public int CalculateMandelbrot() { // CPU-bound 操作 ...

Task.Run 的正确使用方式(3)

在 Task.Run 正确使用方式(2)里,研究了为什么不应该在 CPU-bound 方法实现里使用 Task.Run。相反,我们应该使用 Task.Run 调用该方法。让我们考虑一个更复杂的场景:服务 API 包括获取数据(I/O-bound)和分析处理数据(CPU-bound)两部分。不理想的同步代码class MyService{ publ...

Task.Run 的正确使用方式(2)

我们从一些现有的代码开始,它们以同步方式执行一些复杂计算。class MyService{ public int CalculateMandelbrot() { for (int i = 0; i != 10000000; ++i) { // 一些计算逻辑 } r...

Task.Run 的正确使用方式(1)

要想正确使用 Task.Run,我们得先搞清楚 Task.Run 存在的意义是什么? Task.Run 的真正目的是以异步方式执行 CPU-bound 代码。 为了做到这一点,Task.Run 通过在线程池线程上执行一个方法,该方法执行完后,会返回一个 Task 对象。这听起来似乎很简单。那为什么会存在错误使用 Task.Run 情况呢? 我想部分原因...

WPF 多窗口,多 UI 线程

某些 WPF 应用程序需要多个顶级窗口。 一个线程/ Dispatcher 组合可用于管理多个窗口是完全可接受的,但有时多个线程会执行更好的作业。 尤其当这些窗口中的某一个将有可能要独占线程时,更是如此。Windows 资源管理器以这种方式工作。 每个新资源管理器窗口都属于原始进程,但它是在独立线程的控件下创建的使用 WPF Frame 控件,可以显示...