普通视图

发现新文章,点击刷新页面。
昨天以前首页

std::thread()使用lambda表达式调用函数导致的离奇bug

作者 大致
2025年1月13日 10:44

最近项目升级开发环境,从visual stdio 2017升级到visual stdio 2022,出现奇怪的现象:同样的代码,2017编译出来风平浪静,2022编译出来一运行就是段错误。

我们的函数大概长这样:

class a {
    public:
    void DoThings(std::string str1, std::string str2, int idx){...};
    void Do0(){...};
    void OnInit() {
        std::string str1 = "FileName.txt";
        std::string str2 = "X:\\Dest\\Path\";
        int i = 0;
        std::thread thd = std::thread([&]{DoThings(str1, str2, i);});
        thd.detach();
    };
};

从debug表现来看,是调用线程函数的时候,传入了典型的野指针。但是啊,三个参数,两个是std::string,另外一个是int啊!string有问题可以理解,可int怎么还能错呢?
把参数改成传入前new,调用后delete,自然是解决了。但心里各种不爽,new一个int,脸往哪搁啊!
好在问题定位的范围比较小,只是起线程调用函数这一小块地方。

2017只支持到C++ 11,而2022是C++ 14,看来问题出在这里了。
去找lambda的说明:

For the entities that are captured by reference (with the capture-default [&] or when using the character &, e.g. [&a, &b, &c]), it is unspecified if additional data members are declared in the closure type, but any such additional members must satisfy.

人家说了,你用lambda进行引用捕获的时候,必须保证捕获的成员是安全的。
看到这里差不多明白了,是[&]的锅。[&]的意思是所有参数按照引用的方式捕获。而你的三个变量都是临时变量,传个毛线的引用啊!
如果不安全会怎么样?这玩意儿叫“未定义的行为”,爱咋样咋样。也就是说,我们的写法触发了这种未定义的右值引用行为,人家可以给你实装成保留地址,也可以转换成另外的指针进行实装。故而2017和2022都没错,错的是写代码的人。
继续写个例子验证一下:

#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::mutex g_mtx;

class CTester {
public:
    CTester() {
    };

    virtual ~CTester() {
    };
    void Run() {
        for (int i = 100, j = 1, n = 0; n < 3; i += 100, j += 1, n++) {
            std::thread thd = std::thread([&#038;] { Show(i, j, "std::thread([&#038;] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([&#038;, i, j] { Show(i, j, "std::thread([&#038;, i, j] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([&#038;, j] { Show(i, j, "std::thread([&#038;, j] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([=] { Show(i, j, "std::thread([=] { Show(i, j); }): "); });
            thd.detach();
        }
    }
    void Show(int x, int y, const char* pri) {
        std::lock_guard< std::mutex>lock(g_mtx);
        char szOut[128] = { 0 };
        sprintf(szOut, "pri = %s x=%d y=%d\n", pri, x, y);
        std::cout << szOut;
    }
};

int main()
{
    std::cout << "Test Start" << '\n';
    CTester t;
    t.Run();
    _sleep(500);
    std::cout << "Test End" << '\n';
    int c = getchar();
    return 0;
}

2017的运行结果:
Test Start
pri = std::thread([&] { Show(i, j); }): x=100 y=1
pri = std::thread([&, i, j] { Show(i, j); }): x=100 y=1
pri = std::thread([&, j] { Show(i, j); }): x=100 y=1
pri = std::thread([=] { Show(i, j); }): x=100 y=1
pri = std::thread([&] { Show(i, j); }): x=200 y=2
pri = std::thread([&, i, j] { Show(i, j); }): x=200 y=2
pri = std::thread([&, j] { Show(i, j); }): x=200 y=2
pri = std::thread([=] { Show(i, j); }): x=200 y=2
pri = std::thread([&] { Show(i, j); }): x=300 y=3
pri = std::thread([&, i, j] { Show(i, j); }): x=300 y=3
pri = std::thread([&, j] { Show(i, j); }): x=300 y=3
pri = std::thread([=] { Show(i, j); }): x=300 y=3
Test End

2022的运行结果:
Test Start
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([=] { Show(i, j); }): x=100 y=1
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([&, i, j] { Show(i, j); }): x=100 y=1
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=1
pri = std::thread([&, i, j] { Show(i, j); }): x=200 y=2
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=2
pri = std::thread([=] { Show(i, j); }): x=200 y=2
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=3
pri = std::thread([&, i, j] { Show(i, j); }): x=300 y=3
pri = std::thread([=] { Show(i, j); }): x=300 y=3
Test End

很明显,只要敢给2022(C++ 14)传引用,它就敢给你乱引……

这部分代码是从厂商的例子里抄的。人家写的是

std::thread([&]{Do0();});

只是隐式地捕获一个this!当然写[&]就行了。
友军抄的时候根本不知道方括号是干什么的,只改了后面,才造成了这样的后果。

那么怎么解决呢?写[=]或者[&, str1, str2, i]吗?并不是。
lambda叫啥?“匿名函数”啊!你都要调用真正的函数了,就别整匿名函数那一套了。
正确的std::thread调用类函数的时候应该长这样:

std::thread thrd = std::thread(&a::DoThings, this, str1, str2, i);
thrd.detach();

我是真心的不喜欢lambda。


  • (1):统计表里记作4部是因为有同名,公式如何修改还没想好

2024年一位中年男人的无聊折腾记录

作者 佐仔
2024年12月20日 15:49

这是我这位中年男人的无聊折腾记录吧,每年汇总一次,纯记录,不为其他。

2025年是“佐仔志”博客的第20个年头,不得不感概时间流逝之快。我们都是追逐时代之人,我们也在被时代慢慢所淘汰,不可避免也无可奈何,只能坦然面对,也只能面对。面对AI的发展和时代的变迁,不仅仅是我们个人还是各个行业,都无时无刻地被这个时代所抛弃,并且是特别无情的那种。

折腾这些数码与IT产品,只是我这位中年男人想抓住时代的尾巴的其中一种方式,尽量用一种舒服的心态去面对它,适应它,接纳它和爱上它,也只能这样,我们还是那位抓住时代尾巴的人…也许这只是自我安慰吧。

1、iPhone 16 Pro Max 512G 港版

输入这个型号的时候,我还专程打开手机查看核实,因为就感觉未曾换过新iPhone,也忘记现在最新发布的是 16 还是 15 代,之所有买港版是为了AI,但它并不怎么AI,而新的摄影按钮暂时来说有点多余,没有三星长按最底下横杠启用AI圈图搜索方式。iPhone 16 Pro Max 现已放备用机在使用,上面都是一些金融类APP在上面。

2、三星 Galaxy S24 Ultra、三星 Fold6、三星 W25

安卓阵型手机今年换了三台,但都是三星的,这三台手机我都喜欢,最喜欢依旧是Galaxy S24 Ultra,四四方方的大屏和简洁的后置镜头模组就是吸引我。折叠屏手机三星Fold6 也很优秀,如果把W25的配置放到Fold6上,而外观保留Fold6现在这种设计,那Fold6就是折叠屏手机的完美版,可惜没有。现在使用的是三星W25,它的配置是完美的,但外观我还是喜欢Fold6。

之所以没有买国产那些旗舰手机,是真的无法接受那些手机背面的相机模组。

3、贝尔金 15W 车载磁吸支架

买这个设备还得深吸一口气,因为它的价格真的不低,500元/个,只是一个车载充电器而已,但又为什么要买这么贵的呢?理由很简单:它的整体质感与轻微热量是其他品牌比不了的。现在这个年纪不再轻易去试错,所以买就买最好的,也许这只是我个人的要求。

4、华硕(ASUS)小飞侠信号放大器

买它是因为蹲厕时WIFI信号不好…因为在如厕时能好好刷手机,那就买它了…中继扩展后,信号有了质的提升。不知为什么,这些年有关路由器和交换机,都是选择华硕居多。

5、ANKER 安克 magsafe 磁吸无线充电宝

好看,小巧,实用,1万毫安,足够两台手机一天的使用–这是我买它的原因。为颜值而买单,但别是出差与游玩时使用,也是随背包携带

6、酷态科10号GaN CP120W 氮化镓三口充电器

为了发挥出Thinkpad T14p 2024款笔记本电脑其独显性能,为了不想带ThinkPad那个大砖块电源适配器,所以就挑了它。它还是不错的,外出只需要一个电源适配器既可。随背包携带

7、中兴F50 5G随身WIFI

纯属是有一张多余的电话卡,同时因为Thinkpad T14p无法内置,外出时给小朋友 iPad 联网上网课所买入的,随背包携带

8、ThinkPad T14p AI 笔记本电脑

配置是:酷睿Ultra9 185H 14.5英寸 32G 1TB+1TB(双固态硬盘) 3K RTX4050,它随机带的AI功能激活后就删除了,因为第三方AI比随机带的强得多。买它是为了跑AI与玩游戏使用,小朋友喜欢用它玩模似飞行2024,很是流畅。除了比ThinkPad X1C 重点无其他缺点,并且待机时间还比ThinkPad x1c 要强,在我心中比 Macbook M4 Pro 都要好。

9、石头(roborock) P10S Pro 扫地机器人

很多年前曾有一个石头扫地机器人,但要经常清理毛发,用的次数不多就没再用了。年中,老婆抱怨家务问题,试着买回这台P10S Pro 扫拖一体机器人,使用至今觉得比多年前的机器人好太多了,首先不怎么卷毛发了,一个月清理一次既可,日常只需要两天换一次水既可。如果条件许可以,建议买直排水式的。

10、Apple/苹果 iPad mini6 蜂蜗版 256G

我忘记为什么又买回iPad mini6了,因为去年我曾拥有过一台,后来卖掉后又买回来了。近期比较少用,但它一直放在包里。谁想要的可以联系我,便宜出掉。

11、Apple/苹果 AirPods Pro (第二代)

常规升级…没有什么惊喜,也就那样。

12、Bose QC 消噪耳塞 大鲨系列主动降噪耳机

降噪效果非常好,比 AirPods Pro 还有好,包括音质。但它也有问题:感觉耳机比较闷,同时有几次外部突然有很大的声音(比喻车过下水道盖或坎时发出的声音)被扩音、扩音、扩音到耳内,可想而知有多难受了,同时它的充电盒松松垮垮的,所以以旧换新换了AirPods Pro 二代。

13、佳明(GARMIN)Fenix 8

最终还是把 Apple Watch Ultra (忘记是二代还是三代了,总之是最新的一代)蜂窝版换成了佳明最新 Fenix 8,Watch Ultra 不是不好,是它太重和续航太短,特别是续航问题。而 Fenix 8 除了无法电话之外,其它功能都差不多,特别是运动类的功能,还有它长达2周多的续航,还是戴它舒心,不需要每天惦记着充电。

14、硬酷 R2 Max 万兆电口软路由

这款是买用给到公司使用的,去年买的H3C企业路由器速度上不去,所以就换了这台。这台除了当路由器使用外,还布署了企业AI,方便同事日常查询使用。由于业务的转变,现在客户问的问题实在是太专业了,必须有AI来辅助才行。

15、Homatics Box R 4K Plus 安卓机顶盒

安卓原生机顶盒,可以说是现在最好配置的安卓机顶盒了,配上合适的网络和其开放性,使用起来比Apple TV 4K 舒服多了。

大概就这些了,如果上面有你想买但又在犹豫的,可以留下你的问题,我尽快给你回复。如果刚才你也买了相同的产品,也希望留下你的使用心得。

 

主动和被迫

作者 xrspook
2024年10月1日 08:29

当年今日

直到现在我都觉得分享是一件很快乐的事情,但现在相对于从前来说,我主动分享某些东西的概率会低了很多。生活中的任何东西都可以用来分享,可能是各种见闻,也可能是各种技巧。这种分享我感觉是非常有必要的,因为说不准什么时候就可以帮到别人,那更重要的是,说不准什么时候就可以挽救自己。某个教程做出来,与其说可能会被某些陌生人看到,并让他们觉得有用,还不如说是多年以后的某一天突然自己又遇到了同样的问题,但因为之前已经有了一套解决方案,所以我不需要慌彷徨。现在我越发觉得,这种积累非常有必要。这种积累不限于一个范围,生活的方方面面都需要这种积累。有些时候你会觉得,一些人简直就是个万能型的存在,但你不知道的是他在背后做了多少积累的工作。表面上他很风光的,但背地里,谁也不知道他经历过怎么样的坎坷。绝大多数人都不会把这些自己的艰辛分享出来。我不知道这是为什么。不仅仅是结果,那个过程其实也是很有趣的,而这种有趣,可能只是对自己而言。

有些时候,别人会强迫我进行分享,但我很抗拒那种东西。美其名曰是约稿,但是我打心里抗拒。就像从前,我非常抗拒任务分配下来的征文比赛交作业。主动参加的征文比赛,在我印象之中,只有一次。是华农的某个征文比赛,但是我交上去的是一篇英文,结果邮件居然回复下来说,如果你是个中国人,你就交中文的过来。凭什么中国人就不能交一篇英文参加你们的征文比赛呢?既然文体不限的话,为什么要限制语言?他们可以无视我这种稿件,但是被这般回复让我觉得非常愤怒。写得好不好是一回事,不是你们参赛范围你们可以直接忽略,为什么还得这样?为什么那个时候我会交一篇英文上去,是因为那段时间我所有blog都是用英文写的。我强迫自己那么干,那段时间我的脑子只有英文回路,不知道该怎么用中文直接表达。四六级英语考试里面,我的作文却从来不是老师的菜,每次成绩出来作文都是悲催。为什么高中的时候作文不会有这种待遇,但是四六级英语考试的时候,却被判成那样呢,我搞不懂。我也从来没有主动搞懂过,因为我不喜欢用他们的那些套路,当然我也不知道他们的套路是什么。高中老师没教我那些套路,大学英语的老师也没教给我那些套路。那些套路是辅助机构默认的潜规则,可能那些潜规则也是四六级英语考试老师判卷的潜规则。我从来没上过那种班,但是要我阅读英文的各种材料,从来都不成问题,除非你给我读专四专八的东西。既然生活上的英文没问题,专业期刊的英文没有问题,看电视、看电影也没觉得有什么问题。为什么我就非得用你们八股的那种方式写那些所谓好文呢?!

我自己可以写很多,但当你要求我写的时候,我却不愿意。

闲来无事,又替朋友配台电脑

作者 叶子
2022年6月10日 13:54

其实在这个时候配台电脑属实不算明智,不过奈何朋友确实有刚需。就还是根据他的需求配置了一台电脑。(根据他最基础的需求,我层层加码,哈哈!

CPU:R7 5800X

主板:B550M迫击炮(够用就好

显卡:RTX3060 12G 黑金版 LHR (锁算力

内存条:DDR4 3600 16X2

散热器:利民240 冰封幻境 水冷散热

固态硬盘:三星980 500g

机械硬盘:西数 紫盘 4T

电源:TT额定650W金牌模组

机箱:追风者P360A-白

显示器:AOC-Q27G2S(这个是他自己选择的,我本身是推荐AOC-U27N3C的)

键盘鼠标:入手了达尔优的,现在国产键盘鼠标不比国外品牌差(主要是朋友预算,不然我也是一步到位)

说是给女朋友买来剪剪视频,结果配置逐渐走向游戏,真是男人的嘴,骗人的鬼。

wordpress 获得当前文章真实序号的方法

作者 大致
2024年3月7日 21:43

想在主题里利用一下这个小功能。
那些抄来抄去的都包了浆的所谓办法都是在后续的ID上作文章,根本没有切中问题的本质。因为在wordpress的设计思想中,万物皆post,根本没把ID的增长当回事。想通过限制自动保持、自动版本、限制媒体什么的解决方案是缘木求鱼,根本不治本。
其实“取连续的文章序号”换个思路,就是取“截止当前发表时间,它的前面还有多少篇”这么一个简单问题。刚好wordpress的核心类WP_Query支持根据时间检索,而时间检索的参数里刚好有个’before’。一拍即合。
下面就是代码。注意:此代码要在loop内部使用。

get_real_post_id() {
    if ( !is_single() ){
        return;
    }
    global $post;
    if ( 'publish' !== $post->post_status
		&& 'private' !== $post->post_status) {
        return;
    }
    $args = array(
        'post_type' => 'post',
        'post_status' => array('publish', 'private'),
        'post_per_page' => -1,
        'fields' => 'ids',
        'date_query' => array(
            'before' => $post->post_date,
        ),
    );
    $the_query = new WP_Query($args);
    return $the_query->post_count;
}

简单解释一下:
因为要用到post的ID、post_status和post_date,所以直接用了loop循环中存在的全局变量$post。不用这个全局变量,改用get系列的函数也是一样的。
检索的post类型,我用的是publish和private。这个参是个数组,可以根据需要自行增减。
fields设成ids,意思是返回值接受id。这么写是因为有人说这样可以加快执行速度,并没有实际证据。

date_query是重头戏。
before可以接受的是date string。而无论你的时间格式设成什么样,$post->post_date都刚好是个标准的时间字符串,而且还精确到秒,所以直接放进去即可。
这里其实省略了一个默认参“column”=“post_date”,也就是给before参赋的值。可以选择的还有“post_date_gmt”,“post_modified”,“post_modified_gmt”。都是字面意思,有需要自己替换就行。
还有就是,before默认生成的运算符是小于等于,也就是取得的数包括了当前贴自身。如果要取得除自己以外的数量,自行减1,或者增加参数post__not_in均可达到目的。

对了还有,get_posts跟WP_Query本质上是一个东西,同样的参数,用get_posts调用,也能起到同样的效果。但是实际执行时,get_posts为了得到返回值,需要多执行一次qurey。而使用WP_Query的时候,直接取成员post_count即可,并不需要把post真的检索出来,相对来说效率更高。

此方案的优点是动态获取,删帖或者转换帖子类型,甚至无耻地修改发帖时间,都不会影响序号的连续性。
缺点就是进行了一次数据库操作,会耽误一丢丢时间。不过据说WP_Query默认操作的是缓存,影响应该没那么大。
以上。


  • (1):地球球花演邦女郎的时候已经是五旬老太了,不在讨论之列
  • (2):本作日版发售后3天,任天堂的下一代掌机NDS在北美上市
  • (3):本作两个流行的汉化版都有大问题。一个D商汉化版不能正常存档,另一个汉化版切换到饺子开始的中间几个人物后会死机。
  • (4):五人分工:辽冀鲁,津京,江浙沪,闽粤桂,深圳+长江(武汉)
  • (5):本来还有个汤球球,但他把机器和卡一起卖给了我。

妞宝两岁了

作者 大灰
2023年10月27日 23:30
昨天还是嗷嗷待哺的婴儿,今天已经是调皮捣蛋的小女孩…… 翻出以前宝宝的照片变化真的好大,不得不感叹宝宝变化真快! 现在这个时间段正是最难带的时间,可怕的两岁…… 昨天晚上给宝宝播放儿歌,没想到妞宝可以自己哼唱了,我跟媳妇都很惊讶,看来还得多多陪伴妞宝。 我陪你长大,你陪我变老,愿时光能缓,愿你健康快乐成长。
❌
❌