C++多线程编程1

来看一个例子,

#include <iostream>
#include <thread>
int a = 0;
int b = 0;
void printMessage() {
for (int i = 0; i < 100; i++)
{
std::cout << "Hello from thread A! "<< a++ << std::endl;
}
}

void printMessage1() {
for (int i = 0; i < 100; i++)
{
std::cout << "Hello from thread B! "<< b++ << std::endl;
}
}

int main() {
// 创建一个线程,运行 printMessage 函数
std::thread myThread(printMessage);
std::thread myThread1(printMessage1);

// 等待线程完成
myThread.join();
myThread1.join();

std::cout << "Hello from main thread!" << std::endl;
return 0;
}

Execute 1st output:

Hello from thread A! Hello from thread B! 0
Hello from thread A! 1
Hello from thread A! 2
... ... //输出省略掉了
Hello from thread A! 98
Hello from thread A! 99
0
Hello from thread B! 1
Hello from thread B! 2
... ... //输出省略掉了
Hello from thread B! 98
Hello from thread B! 99
Hello from main thread!

Execute 2rd output:

Hello from thread A! Hello from thread B! 00
Hello from thread A! 1
Hello from thread A! 2
... ... //输出省略掉了
Hello from thread A! 98
Hello from thread A! 99
//这里有一个程序输出的空格
Hello from thread B! 1
Hello from thread B! 2
... ... //输出省略掉了
Hello from thread B! 98
Hello from thread B! 99
Hello from main thread!

可以看到两次输出不一致。实际上把循环加到1000,两个线程的交叉执行调用会更频繁。因为这里程序是并发(Concurrency)执行,即线程在一个核上以’时间切片’的方式交替执行[1]。

Hello from thread A! Hello from thread B! 0
Hello from thread B! 1
Hello from thread B! 1
... ... //输出省略掉了
Hello from thread B! 299
0
Hello from thread A! 1
Hello from thread A! 2
... ... //输出省略掉了
Hello from thread A! 154
Hello from thread A! 155Hello from thread B! 300
Hello from thread B! 301
... ... //输出省略掉了
Hello from thread B! 999
//这里有一个程序输出的空格
Hello from thread A! 156
Hello from thread A! 157
... ... //输出省略掉了
Hello from thread A! 999
Hello from main thread!

[1]参考C++ Concurrency in Action: Practical Multithreading第一章

2023个人最佳

硬件

Dell 键盘 KB216t

今年买了IQUNIX的机械键OG80尝试,觉得机械键盘键程太长。 发现这款便宜Dell的键盘用得更习惯。键程短,键帽不晃动。

软件

Trello

image from Atlassian

很好的事务管理软件,原本是项目管理用,发现做个人事务管理也很不错

电影

灌篮高手


今年电影院看的电影个人觉得有几部不错的,年会不能停,奥本海默,疯狂元素城,满江红。疯狂元素城故事较薄弱但是渲染技术我认为又有了进步。综合还是选灌篮高手,也许是情怀 ;p。

关于做决定的思考

今天早上看到一篇关于雷军年度演讲的新闻报道,https://mp.weixin.qq.com/s/mnC8q5bCCpJDlu2t9tjalA?poc_token=HF8Dm2ajrfktxxTPMqhWI5EBqg0lqoWtgqEu7y4f

其中提到

  • 2021 年他在《我的梦想 我的选择》演讲中鼓励道,「不要怕做选择,干了再说」;

我看到这里突然有些感悟。因为我以前挺不擅长做选择的,感觉自己有选择困难症。会为做一个选择拖上几个周。

现在工作多年经历了更多事件选择后,多了几分感悟。关于选择决定有了一些总结,思考和别人的分享有不谋而合之处。

「不要怕做选择,干了再说」

2021年左右小米应该是对外宣布造车左右的时间点,雷军这篇演讲应该是切合小米造车这个选择做的演讲。造车应该也是小米公司内部一个重大决定,雷军分享这个决定小米开了21天会。

我想开再多的会,做再多的调研,数据支撑。都是对做决定起一个辅助作用,而不是决定性作用。决定性作用的是人。有些决定必然有很多利弊,牵扯到各个因素,怎么去衡量它们,然后拍板呢?我想对大企业来说恐怕也很难,但还是要去做选择。

重要的决定做了调查后,有了数据支撑后,但是无法衡量到所有方面的,需要勇敢的走出那一步。至于后果,后果后面有办法去处理。选择一个选项去做,也比停留在选择面前拖延原地踏步好。因为一旦养成了选择拖延的习惯,容易生活中处处选择拖延,自己过的累不说,而且你的人生容量因为拖延减少了,成长得更少,别人快速做决定做了100件事,你处处选择拖延,做事效率降低,只做了60件事。别人100件事里选择错了20件,你60件里选择错了12件。那别人作对了80件,你作对了48件。1. 因为在做了分析后,做出的选择差别就没有那么大了 2. 错误的选择不代表就没有意义,错误的决定积累经验,困难的道路有时也可以磨练意志。

往往你经历得更多,做的事更多,你的经验更多,能力会更强。

我大学刚毕业那会,自己想去北京工作看一下,自己也坐飞机到北京找到了工作机会。可是回到成都后,却犯难了。父母说北京竞争大,我一个只身过去困难。我自己则考虑我目前新换的工作内容其实也还不错。我的领导说项目前景不错,希望我至少再多看一年再走不迟。多方的选择让我无所适从。刚毕业工作没多久的我,没有做过对我当时的我来说这么大这么难以选择的决定。因此我没有上班为了做这个决定花了2到3周来思考。

最后的结果是去了北京,工作了3年。去北京这段选择,收获了一些东西,当然可以看到也失去了一些东西,无从知晓留在成都会怎样,现在也如同当时一样也无法说哪个选择一定更好。所以两者都可以。

因为人生是一条由各种事件决定构成时间线,不应该因为某个选择原地踏步。所有的这些决定,都构成了你。

再说两个最近的选择:

我最近硬盘掉盘坏掉了,我找了一家淘宝店维修,可是店家收到货后不是开始说好的一口价,而是跟我打了几十分钟的电话,沟通我这个1T的固态盘怎么处理麻烦,重新沟通价格和处理方式。我从言语间感受到商家的一些不坦诚和前后矛盾的地方。我就说把盘退回来(商家标称评估后随时可退),商家又挽留且说筛选数据的方案。我又再淘宝上问了一家,这家坚定的说一口价,且店铺的销售额更好。

我其实有点犯难了,一是花了很多时间沟通,第一个商家也答应用选数据的方式给弄,并且预估成功率也有些保证。二是商家给我不坦率的感觉,还怕他因为我想退货花了他的时间,万一不厚道的做些手脚可能性。三是因为我没有数据恢复的经历,这方面的信息有限,没有太多可帮助选择的信息。让我很犹豫。

因为是否恢复需要给别人一个答复,我和我老婆简单沟通了一下。还是因为商家的不坦诚的印象,让我决定把硬盘退回来。后面结果是第二家拿到盘在最初的报价范围内,淘宝上聊了几句,没有找我电话沟通,很快的两三天就完整的恢复出了数据。

我当然不以结果论推导选择,但可以说的是,做出了决定走出去往往更好,各种结果都有方法去应对,我当时想最坏的后果是数据恢复不出来,对我有影响,但不是致命的影响。

第二个是更小的一个事, 因为这个坏硬盘还在保修期内,我要试试找京东看还能售后不。京东两个取件时间都可能会打乱我的周末时间计划(我们这更改取件时间比较麻烦),也让我没法按已有的信息立刻分辨出哪个时间对我的影响更小。这时我的经历告诉我这种小事,应该快速做决定,因为选择的影响有限。过后来看,选哪个时间其实差别都不大。

去战斗吧,趁还有精力的时候。不要把时间荒废在躺在沙发上,担心未来上,人生由脚步书写。干了再说,不论结果如何,后面有办法去处理。

最后,分享一个关于做决定的视频:How to make faster decisions | The Way We Work, a TED series

https://www.youtube.com/watch?v=cTIUiN6inIQ

P.S. 我还看到一种说法,重大的决定当然不容易。为了减缓这种不容易的决定,应该把做决定的影响因素尽量的分担到平时。让拆解出来这些小的选择和结果来帮助你大的决定,让大的决定成为一个自然的选择,不再困难。

P.S.S. 不要怕做选择不意味这对自己的决定可以不做思考,不负责任。成年人对自己的承担所有结果。而青少年有时不是所有结果都由自己承担,一部分也会由父母承担。青少年做某些决定应当征询父母的意见。

Compiling UnrealEngine Project by Command

I found sometimes I just want to tune a little code. Open full solution file of Visual studio is annoying. If you’re same, you can use below command to compile unreal project code:

UnrealBuildTool.exe YourProjectName Platform BuildConfig -project="[Path]\YourProjectName.uproject"

UnrealBuildTool in the \Engine\Binaries\DotNET folder in your Unreal Engine.

Platform is like Win64, IOS, or Android and so on.

BuildConfig is Development, Debug, or Shipping and so on.

For eg.

UnrealBuildTool.exe MyTestProject Win64 Development -project="C:\UE_Projs\MyTestProject\MyTestProject.uproject"

Need Notice, above cmd is for Standalone, If you want to compile for Editor, should replace the target with [YourProjectNameEditor]

UnrealBuildTool.exe MyTestProjectEditor Win64 Development -project="C:\UE_Projs\MyTestProject\MyTestProject.uproject"

2024年再玩Kindom Rush

五一放假在家,重新玩了一下移动游戏Kindom Rush,只不过是第二代”Frontiers“。第一代已经是很多年前玩的了,第二代今天才玩。玩了一会儿,我感觉离一代13年发布,这么多年过去了,Kindom Rush(系列)依然是移动(手机)游戏的第一梯队。

手机作为移动设备,主要特点是自带便携电源导致电源,另外性能受限。所以不太适合太硬核的游戏。Kindom Rush 2 : Frontiers 在一个硬核程度适中的选择下,不论是玩法,画面,音乐,游戏策略性都做的不错,尤其能耗体验做得非常好。而Kindom Rush之后,手机游戏(主要指中国国产游戏)大多往硬核游戏的方向发展,但这并不是手机游戏所擅长的。所以这可能是我觉得这么多年后,Kindom Rush依然是我以为的手机游戏第一梯队的一个重要原因。

C#内存管理

值类型,引用类型

string,class为引用类型,其他是值类型。值类型分配在栈上[1],引用类型分配在堆上。


堆栈

值类型分配在栈上,局部变量直接存储值。引用类型分配在托管堆,栈上存储一个地址在局部变量引用堆内存。

例如:

Person foo = new Person();

foo存储一个4字节地址,引用堆内存Person分配的空间。

int bar = new int(); 

因为bar是值类型,所以new分配在栈上[2],并且值类型bar直接存储的数据。

[1]《深入理解C#》中提到, 值类型一定分配在栈上并不准确。值类型是跟声明的对象一起放在一起存储的,作为引用类型class的成员的时候跟着class放在堆上。

[2]这点和C++语言不同,new是堆分配关键字,调用new即分配在堆上。


垃圾回收

堆上的内存没有引用会在垃圾回收的时候回收,垃圾回收会暂停其他线程,会压缩重排堆内存以减少碎片。

装箱拆箱

装箱会在堆上分配内存,把栈上的值拷贝的堆上。

拆箱会在栈上分配对象,把堆上的内存拷贝到栈上。栈上的类型和拆箱的类型不一致会报错。

2D坐标系中旋转一个点

2D坐标系中一个点x, y逆时针旋转角度θ后新坐标点x’, y’位置。要用到三角和公式:

cos(Ф+θ) = cosФcosθ – sinФsinθ

sin(Ф+θ) = sinФcosθ + cosФsinθ

查看源图像
Image from 2-D Transformations. Kurt Akeley CS248 Lecture 7 

由上图,运用三角和公式后,将r·cosФ代入为x, r·sinФ代入为y,就可以通过x, y坐标得出新的x’, y’坐标了:

x’ = xcosθ – ysinθ

y’ = xsinθ + ycosθ

顺时针旋转可以理解为逆时针一个负角度,根据sin(),cos()的奇偶性,即sin(-θ)=-sin(θ),cos(-θ)=cos(θ),可得顺时针旋转的变换公式:

x’ = xcosθ + ysinθ

y’ = ycosθ – xsinθ

另一个问题,三角和公式怎么推导出来。下图用几何法证明了三角和公式的推导:

Reference:

平面内直角坐标系中坐标旋转变换公式https://blog.csdn.net/sinat_33425327/article/details/78333946

两角和差的正余弦公式的若干证明方法https://zhuanlan.zhihu.com/p/361839484

https://www.slideserve.com/kirima/chapter-4-2-d-transformations-cartesian-coordinates

关于屎山代码

业务逻辑代码有时候会成为接手的人觉得特别差的屎山代码。
多为以下原因:

  • 第一手实现者,或因为能力或思考不足,设计实现问题
  • 经多人经手,需求反复改,或赶deadline,先让功能工作起来,后期也没时间再优化

可能我们因为一些需求会需要修改屎山代码,个人经验,做优化的时候,步子不要迈太大,特别是遇到屎山代码的时候。不要一次优化原本内容,还同时也把看起来顺手就可以改的业务逻辑也优化了。特别是一些tricky代码,改了容易出一些特定条件的bug。还是分开一步步做比较好。

我不是说应该有屎山代码或为其辩护,只是说我们应该正确看待。
程序员应该还是要提高对自己的代码要求。但世间的事却又事多元化,就如当前语境下“圣母”带有贬义一样。

听说全球大公司微软内部也有一堆很多Could work但是程序员不想碰的屎山代码。

UE4迭代器删除元素

在一个Unreal Engine的项目中,发现TArray<AActor>迭代器循环,actor::destroy会在数组中移除该元素,和element.remove()一样, 引起迭代器失效,很奇怪。经调查原来是因为在Actor::endplay里代码把自身从数组中移除了。我因为之前没有在迭代器里删除过元素,在查这个Bug过程中,我看了下数组Remove的代码。

TArray Remove Implementation

C++ vector::erase操作删除一个元素会导致后面所有的元素都会向前移动一个位置,这些元素的地址发生了变化,所以当前位置到容器末尾元素的所有迭代器全部失效。

这里,由于移除元素,移除元素后面的元素都向前移动,所以导致第二个元素3无法被移除掉。