推荐 最新
万码用户

oceanbase/miniob

该项目是 OceanBase 团队基于华中科技大学数据库课程原型,联合多所高校重新开发的、从零上手数据库的学习项目。它结构简单、代码简洁,不仅有文字讲解和视频教程,还有由浅入深的题目。通过理论+实战的方式,帮忙初学者迅速掌握内核模块功能和协同关系,提高工程编码能力,有助于在面试和工作中脱颖而出。

18
0
0
浏览量74
byte10

libcimbar

该项目提供了一种新颖的数据传输方式,通过显示条形码并使用摄像头进行传输,无需网络或蓝牙连接。它使用 C++ 编写,并依赖 OpenCV 和 GLFW 等库,内置的编码器可以生成类似二维码的动态动画,用户在手机上安装解码应用后,通过摄像头扫描即可成功接收数据,传输文件的最大限制为 33 MB。

c++
12
0
0
浏览量58
byte10

ardupilot

这是一个全面的自动驾驶开源项目,可用于开发和控制多种类型的无人驾驶系统。 它提供了导航、躲避障碍物、飞行控制等自动驾驶功能,支持多种类型的车辆系统、硬件平台和传感器,适用于无人机、无人车、无人船和无人潜艇等无人驾驶系统的开发与研究。

10
0
0
浏览量53
万码用户

KDE/krita

这是一款强大且完全免费,无需注册、没有广告、试用期和商用限制的数字绘画工具,让每一位画师都可以自由地进行创作。它可用于绘制概念草图、插画、漫画、动画、接景和 3D 贴图,支持数位板、压感、防抖、图层、滤镜、色彩管理等功能,适用于 Windows、Linux、macOS 操作系统。

c++
7
0
0
浏览量43
邦纳娜娜

CLion控制台cin<<中文无法输入,但是可以粘贴进去,如何修复?

为什么clion的控制台里面输入不了中文,但是可以用剪切板粘贴 !"image.png" (https://wmlx-new-image.oss-cn-shanghai.aliyuncs.com/images/20250121/d2ab415bbaf44de623a791f843082844.png) 我在使用clion学习c++的时候,使用cin>> 想在控制台输入中文但是输不进去 https://wmlx-new-image.oss-cn-shanghai.aliyuncs.com/images/20250121/628596e61dc76277351332f9c4bd3135.png 换过输入法,搜狗qq都试过,在网上也没找到相关资料, 我感觉是因为,输入法打汉字时要先输入字母,然后在把字母替换,但是控制台里面好像没法输入字母 "找到的另一个类似的回答,评论区的朋友研究觉得是版本问题,可能全家桶都出了这个问个题" (https://link.segmentfault.com/?enc=MBY6pPBcu%2Fni1AFHpcdP1w%3D%3D.6FZvDnq0kV%2Fp34oPz9SV7snoS9x%2BtMWQv1B0dkZBif2uvUoztVlZ37g0P9GJAoWrQstPzNUBMvKyfJw09rAozg%3D%3D)

15
1
0
浏览量423
周舟莫UI设计

如何查看一个动态链接库中的依赖的其他动态链接库是绝对地址引用还是名字引用?

╰─➤ ldd libx264-2881b7ff.so.164 linux-vdso.so.1 (0x00007fff1bec0000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9f37919000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9f37d5c000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9f37d57000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9f37600000) /lib64/ld-linux-x86-64.so.2 (0x00007f9f37d77000) 比如上面的输出,哪些是「绝对」,哪些是「相对」呢? 我觉得 "/lib64/ld-linux-x86-64.so.2 (0x00007f9f37d77000)" 是绝对 "linux-vdso.so.1 (0x00007fff1bec0000)" 是相对 但是类似 "libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9f37d5c000)" 这样的算绝对还是相对引用呢?这里的 "=>" 表示什么含义呢? *** 我有一个问题,就是「如果没有 / ,那么就按找一个特定的方式在在磁盘上寻找这个文件。这个时候,ldd 会用 => 显示寻找的结果。」,我安装了一个 pyav,通过 "pip install av" 安装,然后我查看 av 的一个动态链接库 "ldd _core.cpython-310-x86_64-linux-gnu.so" ,发现这个动态链接库里面的 => 都是指向 "/home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs" 里面的某个文件,我不清楚这个「特定的方式」是什么? (svddb_sdk) ╭─pon@admini ~/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av ╰─➤ ldd _core.cpython-310-x86_64-linux-gnu.so linux-vdso.so.1 (0x00007ffc8db53000) libavformat-20d4e1d0.so.59.27.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavformat-20d4e1d0.so.59.27.100 (0x00007fd001254000) libavcodec-8a88085f.so.59.37.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavcodec-8a88085f.so.59.37.100 (0x00007fcffff37000) libavdevice-ed40abdd.so.59.7.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavdevice-ed40abdd.so.59.7.100 (0x00007fcffff18000) libavutil-1701948d.so.57.28.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavutil-1701948d.so.57.28.100 (0x00007fcfffd3a000) libavfilter-2d5314ec.so.8.44.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavfilter-2d5314ec.so.8.44.100 (0x00007fcfff8e9000) libswscale-a6aee226.so.6.7.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libswscale-a6aee226.so.6.7.100 (0x00007fcfff846000) libswresample-b868a3ce.so.4.7.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libswresample-b868a3ce.so.4.7.100 (0x00007fcfff824000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fcfff7f5000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcfff603000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcfff4b4000) libxml2-47a785fa.so.2.9.13 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxml2-47a785fa.so.2.9.13 (0x00007fcfff328000) libbluray-69850b93.so.2.1.2 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libbluray-69850b93.so.2.1.2 (0x00007fcfff2d3000) libgmp-dbb9f291.so.10.4.1 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libgmp-dbb9f291.so.10.4.1 (0x00007fcfff25a000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fcfff23e000) libgnutls-58994bd7.so.30.31.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libgnutls-58994bd7.so.30.31.0 (0x00007fcfff005000) libvpx-c1705a7a.so.7.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libvpx-c1705a7a.so.7.0.0 (0x00007fcffedf0000) liblzma-f3a5963b.so.5.2.5 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/liblzma-f3a5963b.so.5.2.5 (0x00007fcffedc5000) libdav1d-df61568f.so.5.1.1 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libdav1d-df61568f.so.5.1.1 (0x00007fcffebff000) libopencore-amrwb-9db94aa9.so.0.0.3 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libopencore-amrwb-9db94aa9.so.0.0.3 (0x00007fcffebe9000) libaom-e9efed4a.so.3.2.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libaom-e9efed4a.so.3.2.0 (0x00007fcffe4a9000) libmp3lame-3ecc6556.so.0.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libmp3lame-3ecc6556.so.0.0.0 (0x00007fcffe415000) libopencore-amrnb-393dbae2.so.0.0.3 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libopencore-amrnb-393dbae2.so.0.0.3 (0x00007fcffe3e7000) libopenjp2-0d101c52.so.2.4.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libopenjp2-0d101c52.so.2.4.0 (0x00007fcffe363000) libopus-70bda348.so.0.8.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libopus-70bda348.so.0.8.0 (0x00007fcffe307000) libspeex-b6a53f7a.so.1.5.1 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libspeex-b6a53f7a.so.1.5.1 (0x00007fcffe2eb000) libtheoraenc-276df146.so.1.1.2 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libtheoraenc-276df146.so.1.1.2 (0x00007fcffe2a9000) libtheoradec-f01ee89e.so.1.1.4 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libtheoradec-f01ee89e.so.1.1.4 (0x00007fcffe287000) libtwolame-72d74ef7.so.0.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libtwolame-72d74ef7.so.0.0.0 (0x00007fcffe25e000) libvorbis-f4a9a6fd.so.0.4.9 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libvorbis-f4a9a6fd.so.0.4.9 (0x00007fcffe221000) libvorbisenc-0d9d5bdf.so.2.0.12 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libvorbisenc-0d9d5bdf.so.2.0.12 (0x00007fcffe170000) libx264-2881b7ff.so.164 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libx264-2881b7ff.so.164 (0x00007fcffde4d000) libx265-d8690e8d.so.199 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libx265-d8690e8d.so.199 (0x00007fcffcbc1000) libxvidcore-d29bca61.so.4.3 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxvidcore-d29bca61.so.4.3 (0x00007fcffcab3000) libxcb-65da195c.so.1.1.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxcb-65da195c.so.1.1.0 (0x00007fcffc880000) libxcb-shm-7a199f70.so.0.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxcb-shm-7a199f70.so.0.0.0 (0x00007fcffc679000) libxcb-shape-25c2b258.so.0.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxcb-shape-25c2b258.so.0.0.0 (0x00007fcffc472000) libxcb-xfixes-9be3ba6f.so.0.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libxcb-xfixes-9be3ba6f.so.0.0.0 (0x00007fcffc262000) libpostproc-9d3ac700.so.56.6.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libpostproc-9d3ac700.so.56.6.100 (0x00007fcffc240000) libass-8499ab98.so.9.1.3 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libass-8499ab98.so.9.1.3 (0x00007fcffc1fe000) libfontconfig-99ba2620.so.1.12.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libfontconfig-99ba2620.so.1.12.0 (0x00007fcffc1aa000) libfreetype-19bd6cfb.so.6.17.1 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libfreetype-19bd6cfb.so.6.17.1 (0x00007fcffc0f9000) /lib64/ld-linux-x86-64.so.2 (0x00007fd0014fe000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fcffc0f1000) libunistring-aeeab030.so.2.1.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libunistring-aeeab030.so.2.1.0 (0x00007fcffbf6a000) libnettle-cb75a9d6.so.8.4 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libnettle-cb75a9d6.so.8.4 (0x00007fcffbf1f000) libhogweed-5d799758.so.6.4 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libhogweed-5d799758.so.6.4 (0x00007fcffbece000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fcffbcec000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fcffbccf000) libogg-bbd52b06.so.0.8.5 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libogg-bbd52b06.so.0.8.5 (0x00007fcffbcc3000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fcffbcb9000) libXau-00ec42fe.so.6.0.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libXau-00ec42fe.so.6.0.0 (0x00007fcffbab4000) libfribidi-baef595b.so.0.4.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libfribidi-baef595b.so.0.4.0 (0x00007fcffba91000) libharfbuzz-d40f381a.so.0.40100.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libharfbuzz-d40f381a.so.0.40100.0 (0x00007fcffb97d000) libuuid-f64cda11.so.1.3.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libuuid-f64cda11.so.1.3.0 (0x00007fcffb777000) libpng16-1f529098.so.16.37.0 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libpng16-1f529098.so.16.37.0 (0x00007fcffb746000) 比如其中的 "libavformat-20d4e1d0.so.59.27.100 => /home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavformat-20d4e1d0.so.59.27.100 (0x00007fd001254000)" 这个是按照什么规定让 "libavformat-20d4e1d0.so.59.27.100" 指向 "/home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/libavformat-20d4e1d0.so.59.27.100 (0x00007fd001254000)" 的呢?按理来说,"/home/pon/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av/./../av.libs/" 不应该存在于系统的默认搜索路径中吧?而且我也没有添加类似 LD_LIBRARY_PATH、LD_PRELOAD、LD_LIBRARYN、LD_DEBUG、LD_BIND_NOW 的环境变量 (svddb_sdk) ╭─pon@admini ~/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av ╰─➤ printenv | grep LD_ 1 ↵ (svddb_sdk) ╭─pon@admini ~/.local/share/virtualenvs/svddb_sdk-qAt4tE2E/lib/python3.10/site-packages/av *** 如果使用 readelf 查看,就是下面这样 ─➤ readelf -d _core.cpython-310-x86_64-linux-gnu.so 1 ↵ Dynamic section at offset 0x2d000 contains 33 entries: Tag Type Name/Value 0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../av.libs] 0x0000000000000001 (NEEDED) Shared library: [libavformat-20d4e1d0.so.59.27.100] 0x0000000000000001 (NEEDED) Shared library: [libavcodec-8a88085f.so.59.37.100] 0x0000000000000001 (NEEDED) Shared library: [libavdevice-ed40abdd.so.59.7.100] 0x0000000000000001 (NEEDED) Shared library: [libavutil-1701948d.so.57.28.100] 0x0000000000000001 (NEEDED) Shared library: [libavfilter-2d5314ec.so.8.44.100] 0x0000000000000001 (NEEDED) Shared library: [libswscale-a6aee226.so.6.7.100] 0x0000000000000001 (NEEDED) Shared library: [libswresample-b868a3ce.so.4.7.100] 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] 0x000000000000000c (INIT) 0x3000 0x000000000000000d (FINI) 0x63c4 0x0000000000000019 (INIT_ARRAY) 0x8cd8 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) 0x000000000000001a (FINI_ARRAY) 0x8ce0 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) 0x000000006ffffef5 (GNU_HASH) 0xb358 0x0000000000000005 (STRTAB) 0x12250 0x0000000000000006 (SYMTAB) 0xa8d8 0x000000000000000a (STRSZ) 2479 (bytes) 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0x9000 0x0000000000000002 (PLTRELSZ) 2016 (bytes) 0x0000000000000014 (PLTREL) RELA 0x0000000000000017 (JMPREL) 0x1e40 0x0000000000000007 (RELA) 0x17b0 0x0000000000000008 (RELASZ) 1680 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x000000006ffffffe (VERNEED) 0x16b0 0x000000006fffffff (VERNEEDNUM) 8 0x000000006ffffff0 (VERSYM) 0x15ce 0x000000006ffffff9 (RELACOUNT) 44 0x0000000000000000 (NULL) 0x0

17
1
0
浏览量323
代码剑客行

极坐标下二重积分问题?

"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241222/ef0cdfa83c511a592582e49b76aecfef.png) "image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241222/e7a57d8f9ba8d3cc427881e06589f88c.png) 一道很简单的二重积分问题,请问大佬,不按对称性的解法,按常规解法怎么算出结果的? 我算的总是不对... 还有对称性的解法, 为什么关于y = 0对称就能推出上图的表达式。 没找到对称性关于这点的知识点。 "45a554ef38e56a64d4e534dff2566cb.jpg" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241222/6895db72b2458e7b6fb6b37870ebb550.png)

15
1
0
浏览量328
菜鸟码转

2.线程同步与异步

线程同步操作:C++ 标准库提供了以下几种线程同步的方式:互斥量(支持超时加锁、递归加锁);读写锁(共享互斥量,也支持超时加锁);互斥量包装器(基于 RAII 的思想);条件变量;信号量(二元信号量、计数信号量);barrier;call_once;不同的同步方式具有不同的使用场景和性能,实际使用时根据不同的场景选择不同的同步方式,分别就几种以上几种方式进行简要介绍:1.互斥量(mutex):互斥量(mutex)是防止同时访问共享资源的程序对象。为避免线程更新共享变量时所出现问题,必须使用互斥量(mutex 是 mutual exclusion 的缩写)来确保同时仅有一个线程可以访问某项共享资源。 即使用互斥量来实现原子访问操作,防止多个线程对临界区同时操作而产生不一致的问题。mutex 只有锁定(locked)和未锁定(unlocked)两种状态。任何时候,至多只有一个线程可以锁定互斥量。试图对已经锁定的互斥量再次加锁,将可能阻塞线程或者报错失败,mutex 的底层可能封装的是操作系统 spinlock,不同的操作系统下可能有不同的实现。C++ 中关于 mutex 的头文件为 #include <mutex>。C++#include <iostream> #include <thread> #include <mutex> std::mutex mtx; void print_block (int n, char c) { mtx.lock(); for (int i=0; i<n; ++i) { std::cout << c; } std::cout << '\n'; mtx.unlock(); } int main () { std::thread th1 (print_block,50,'*'); std::thread th2 (print_block,50,'$'); th1.join(); th2.join(); return 0; } /* **************************************** $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */C++ 中还定义了 timed_mutex:在 mutex 的基础上增加了超时加锁的功能。recursive_mutex:在 mutex 的基础上增加了递归加锁的功能(此时,lock() 函数可以被同一线程在不释放锁的情况下多次调用)。C++std::recursive_mutex mtx; void fun1() { mtx.lock(); mtx.unlock(); } void fun2() { mtx.lock(); fun1(); // recursive lock mtx.unlock(); };2.共享互斥量:std::shared_mutex 是 C++ 17 标准中引入的,由 unique_lock 和 shared_lock 两个类模板配合 shared_mutex 使用,主要用于读写共享锁。unique_lock 用于写入时加锁,shared_lock 用于读取时加锁。对象在构造时自动对 std::shared_mutex 加锁,析构时自动对其解锁。头文件主要包含在 #include <shared_mutex>。shared_mutex 可用于保护共享数据不被多个线程同时访问。与其他便于独占访问的互斥锁类型相比,shared_mutex 具有两个访问级别:shared:多个线程可以共享同一个互斥锁的所有权。exclusive :只有一个线程可以拥有互斥锁。共享互斥锁通常用于多个读取操作可以同时访问同一资源而不会导致数据竞争,但只有一个写入操作的场景。C++#include <iostream> #include <mutex> // For std::unique_lock #include <shared_mutex> #include <thread> class ThreadSafeCounter { public: ThreadSafeCounter() = default; // 多个线程可以同时读取 countter 计数 unsigned int get() const { std::shared_lock lock(mutex_); return value_; } // 只有1个线程可以修改 countter 计数 void increment() { std::unique_lock lock(mutex_); value_++; } // 只有1个线程可以修改 countter 计数 void reset() { std::unique_lock lock(mutex_); value_ = 0; } private: mutable std::shared_mutex mutex_; unsigned int value_ = 0; }; int main() { ThreadSafeCounter counter; auto increment_and_print = [&counter]() { for (int i = 0; i < 3; i++) { counter.increment(); std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n'; // Note: Writing to std::cout actually needs to be synchronized as well // by another std::mutex. This has been omitted to keep the example small. } }; std::thread thread1(increment_and_print); std::thread thread2(increment_and_print); thread1.join(); thread2.join(); } /* 139677317637888 2 139677317637888 3 139677309245184 4 139677309245184 5 139677309245184 6 139677317637888 6 */我们可以看到 increment 同时只能有一个线程对计数进行增加,但可能同时存在多个线程读取同一个计数。shared_timed_mutex 是在 shared_mutex 的基础上增加了超时加锁的功能。3.互斥量包装器lock_guard:使用了 RAII 的机制对互斥量进行类模板封装,构造时加锁,析构时解锁。C++#include <mutex> std::mutex mtx; void f() { const std::lock_guard<std::mutex> lock(mtx); // ... // mtx is automatically released when lock goes out of scope }互斥量包装器对比原生的 mutex 来说,创建即加锁,作用域结束自动析构并解锁,无需手动解锁。缺点是不能中途解锁,不支持复制和移动。在需要加锁的地方,只需要任意实例化一个 lock_guard,调用构造函数成功上锁,出作用域时则 lock_guard 对象会被销毁,调用析构函数自动解锁可以有效避免死锁问题,但是提供的功能单一且不够灵活。unique_lock:unique_lock 类模板也是采用 RAII 的方式对锁进行了封装,并且也是以独占所有权的方式管理 mutex 对象的上锁和解锁操作,即其对象之间不能发生拷贝。在构造(或移动 move 赋值)时,unique_lock 对象需要传递一个 mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 mutex 对象的上锁和解锁操作。使用以上类型互斥量实例化 unique_lock 的对象时,自动调用构造函数上锁,unique_lock 对象销毁时自动调用析构函数解锁,可以很方便的防止死锁问题。与 lock_guard 不同的是,unique_lock 更加的灵活,提供了更多的成员函数:上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until 和 unlock;修改操作:支持移动赋值、交换(swap:与另一个 unique_lock 对象互换所管理的互斥量所有权)、释放(release:返回它所管理的互斥量对象的指针,并释放所有权)。获取属性:owns_lock (返回当前对象是否上了锁)、operator bool() (与 owns_lock() 的功能相同)、mutex(返回当前 unique_lock 所管理的互斥量的指针)。4.条件变量(condition variable):在 C++ 11 以后,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。C++ 中包含的头文件在 #include <condition_variable> 中。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程因等待 条件变量的条件成立 而挂起;另外一个线程使 条件成立 从而给出唤醒线程的信号,从而唤醒被等待的线程;为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是 std::mutex,并且管理这个锁只能是 std::unique_lock std::mutex 等 RAII 模板类。分别是使用以下两个方法实现:等待条件成立使用的是 condition_variable 类成员 wait 、wait_for 或 wait_until。唤醒信号使用的是 condition_variable 类成员 notify_one 或者 notify_all 函数。我们可以看到 wait 函数如下:C++template< class Predicate > void wait( std::unique_lock<std::mutex>& lock, Predicate stop_waiting );线程会一直挂起,直到 stop_waiting 为 true 为止。程序示例如下:C++#include <iostream> #include <string> #include <thread> #include <mutex> #include <condition_variable> std::mutex m; std::condition_variable cv; std::string data; bool ready = false; bool processed = false; void worker_thread() { std::unique_lock<std::mutex> lk(m); // worker 线程等待 ready cv.wait(lk, []{return ready;}); // 唤醒执行 std::cout << "Worker thread is processing data\n"; data += " after processing"; // processed 设置为 true, 唤醒 main 线程 processed = true; std::cout << "Worker thread signals data processing completed\n"; // 释放锁,防止再次被唤醒。 lk.unlock(); // 唤醒 main 线程 cv.notify_one(); } int main() { std::thread worker(worker_thread); // 让 worker 线程先执行,再进行唤醒,否则可能出现 ready = true 先于 worker 线程的执行 worker.detach(); data = "Example data"; // 设置 ready 为 true, 唤醒 worker 线程 { std::lock_guard<std::mutex> lk(m); ready = true; std::cout << "main() signals data ready for processing\n"; } // 唤醒 worker 线程 cv.notify_one(); // 等待 worker 线程 { std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return processed;}); } std::cout << "Back in main(), data = " << data << '\n'; return 0; }5.信号量:C++ 20 中添加了 C++ 中的信号量为二元信号量与计数信号量,二元信号量实际为计数信号量模板的特化。binary_semaphore:二元信号量类似于互斥量,信号量只有 0 与 1 。counting_semaphore:计数信号量所有关于信号量的定义参考头文件 #include <semaphore>,计数信号量是一种轻量级同步原语,可以控制对共享资源的访问。与 std::mutex 不同的是,acounting_semaphore 至少允许 LeastMaxValue 并发访问者对同一资源进行多个并发访问。Acounting_semaphore 包含一个由构造函数初始化的内部计数器。该计数器可以通过 acquire() 获取资源访问权限,并通过调用 release() 来释放资源从而递增计数器。当计数器为零时,调用 acquire() 时就会阻塞直到计数器增加,但是调用 try_acquire( ) 不阻塞;try_acquire_for() 和 try_acquire_until() 阻塞直到计数器增加或达到超时。C++#include <iostream> #include <thread> #include <chrono> #include <semaphore> std::binary_semaphore smphSignalMainToThread{0}, smphSignalThreadToMain{0}; void ThreadProc() { // 第一次进入阻塞 smphSignalMainToThread.acquire(); std::cout << "[thread] Got the signal\n"; // response message using namespace std::literals; std::this_thread::sleep_for(3s); std::cout << "[thread] Send the signal\n"; // message // 唤醒 main 线程 smphSignalThreadToMain.release(); } int main() { std::thread thrWorker(ThreadProc); std::cout << "[main] Send the signal\n"; // message // 唤醒 ThreadProc smphSignalMainToThread.release(); // main 线程阻塞 smphSignalThreadToMain.acquire(); std::cout << "[main] Got the signal\n"; // response message thrWorker.join(); } /* [main] Send the signal [thread] Got the signal [thread] Send the signal [main] Got the signal */6.barrier:C++ 20 以后支持 latch 与 barrier,他们同样可以用来线程同步。latch:类 latch 是 std::ptrdiff_t 类型的向下计数器,可用于同步线程。计数器的值在创建时初始化。线程可能会阻塞在锁存器上,直到计数器减为零。不能增加或重置计数器,这使得锁存器创建后不可重用。其内部维护着一个计数器,当计数不为 0 时,所有参与者(线程)都将阻塞在等待操作处,计数为 0 时,解除阻塞。计数器不可重置或增加,故它是一次性的,不可重用。与 std::barrier 不同,std::latch 参与线程可以多次递减。C++#include <latch> std::latch work_done(4); work_done.count_down(); // decrements the counter in a non-blocking manner work_done.wait(); // blocks until the counter reaches zero bool ok = work_done.try_wait(); // tests if the internal counter equals zero work_done.arrive_and_wait(); // decrements the counter and blocks until it reaches zerobarrier:类似于 latch,它会阻塞线程直到所有参与者线程都到达一个同步点,直到预期数量的线程到达设定的值则会接触阻塞。与 latch 不同的是,它是可重用的。一个 barrier 的生命周期包含多个阶段,每个阶段都定义了一个同步点。一个 barrier 阶段包含:期望计数(设创建时指定的计数为 n),当期望计数不为 0 时,参与者将阻塞于等待操作处;当期望计数为 0 时,会执行创建 barrier 时指定的阶段完成步骤,然后解除阻塞所有阻塞于同步点的参与者线程。当阶段完成步骤执行完成后,会重置期望计数为 n - 调用arrive_and_drop() 的次数,然后开始下一个阶段。C++#include <barrier> #include <iostream> #include <string> #include <thread> #include <vector> int main() { const auto workers = { "anil", "busara", "carl" }; auto on_completion = []() noexcept { // locking not needed here static auto phase = "... done\n" "Cleaning up...\n"; std::cout << phase; phase = "... done\n"; }; std::barrier sync_point(std::ssize(workers), on_completion); auto work = [&](std::string name) { std::string product = " " + name + " worked\n"; std::cout << product; // ok, op<< call is atomic sync_point.arrive_and_wait(); // 全部到达后,进行下一阶段 product = " " + name + " cleaned\n"; std::cout << product; sync_point.arrive_and_wait(); }; std::cout << "Starting...\n"; std::vector<std::thread> threads; for (auto const& worker : workers) { threads.emplace_back(work, worker); } for (auto& thread : threads) { thread.join(); } } /* Starting... anil worked carl worked busara worked ... done Cleaning up... busara cleaned anil cleaned carl cleaned ... done */7.call_once:C++ 11 以后支持 call_once。确保某个操作只被执行一次(成功执行才算),即使是多线程环境下也确保只执行一次。C++template< class Callable, class... Args > void call_once( std::once_flag& flag, Callable&& f, Args&&... args );如果在 call_once 被调用时,flag 表明 f 已经被调用,则 call_once 立即返回(这种调用 call_once 称为被动)。C++#include <iostream> #include <thread> #include <mutex> std::once_flag flag1, flag2; void simple_do_once() { std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; }); } int main() { std::thread st1(simple_do_once); std::thread st2(simple_do_once); std::thread st3(simple_do_once); std::thread st4(simple_do_once); st1.join(); st2.join(); st3.join(); st4.join(); } /* Simple example: called once */

C++
0
0
0
浏览量2156
菜鸟码转

1.C++ 条件变量

1.条件变量(condition variable):在 C 语言中我们使用 pthread_cond_wait 函数作为条件变量,它是由操作系统实现的条件变量,需要详细了解它的运行机制就可以了解 C++ 中条件变量的实现。在 C++ 11 以后,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。C++ 中包含的头文件在 #include <condition_variable> 中。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程因等待条件变量的条件成立而挂起;另外一个线程使条件成立从而给出唤醒线程的信号,从而唤醒被等待的线程;为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是 std::mutex,并且管理这个锁只能是 std::unique_lock std::mutex 等 RAII 模板类。分别是使用以下两个方法实现:等待条件成立使用的是 condition_variable 类成员 wait、wait_for 或 wait_until。唤醒信号使用的是 condition_variable 类成员 notify_one 或者 notify_all 函数。condition_variable 支持的函数如下:构造函数: 它只有默认构造函数,拷贝构造函数和赋值符号重载均被禁止 condition_variable(const condition_variable&) = delete;,operator= [delete];wait:wait 目前支持 wait,wait_for,wait_until 等三种操作,分别对应不同的场景:wait: 对应的函数原型为:C++void wait( std::unique_lock<std::mutex>& lock ); 当前线程执行时就被阻塞,直到等到被 notify 唤醒。在阻塞线程的那一刻,该函数自动调用 lck.unlock(),允许其他被 lck 锁定的线程继续运行。阻塞时被一旦某个线程 notify 时,实际可能为虚假唤醒,该函数将解除阻塞并调用 lck.lock(),获取互斥锁。C++template <class Predicate> void wait (unique_lock<mutex>& lck, Predicate pred);调用时检查 pred,如果为 false, 则阻塞线程,并且调用 lock.unlock(), 否则,继续执行。阻塞时被一旦某个线程 notify 时,实际可能为虚假唤醒,该函数将再次检查 pred,如果为 true,则解除阻塞并调用 lck.lock(),获取互斥锁;否则继续阻塞线程。wait_for: 对应的函数原型为:C++template< class Rep, class Period, class Predicate > bool wait_for( std::unique_lock<std::mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate stop_waiting);调用时,检查两个条件是否满足: stop_waiting 返回是否为 true,时间是否超时,如果两个条件都不满足,则阻塞线程,并调用 lock.unlock(), 否则,到达一定等待时间或满足条件被唤醒。注意等待超过时间段后自动唤醒,判断条件一般需要使用者自己在合适的时候判断,并通过 notify_one() 或 notify_all() 唤醒,所以在使用时应当通过判断返回值来检测是其由于超时返回还是被正常唤醒,即状态是否为 std::cv_status::timeout。程序示例如下:C++#include <iostream> #include <atomic> #include <condition_variable> #include <thread> #include <chrono> using namespace std::chrono_literals; std::condition_variable cv; std::mutex cv_m; int i; void waits(int idx) { std::unique_lock<std::mutex> lk(cv_m); if (cv.wait_for(lk, idx*100ms, []{return i == 1;})) { std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n'; } else { std::cerr << "Thread " << idx << " timed out. i == " << i << '\n'; } } void signals() { std::this_thread::sleep_for(120ms); std::cerr << "Notifying...\n"; cv.notify_all(); std::this_thread::sleep_for(100ms); { std::lock_guard<std::mutex> lk(cv_m); i = 1; } std::cerr << "Notifying again...\n"; cv.notify_all(); } int main() { std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals); t1.join(); // t1 等待 100ms 后未被唤醒,自动超时; t2.join(); // t2 在 120 ms 处被唤醒,但 condition 未满足,再此进入阻塞,200ms 时由于超时返回 t3.join(); // t3 在 120 ms 处被唤醒,但 condition 未满足,再此进入阻塞,220ms 时被 notify ,正常返回。 t4.join(); } /* Thread 1 timed out. i == 0 Notifying... Thread 2 timed out. i == 0 Notifying again... Thread 3 timed out. i == 0 */wait_until: 对应的函数原型为:C++template <class Predicate> void wait (unique_lock<mutex>& lck, Predicate pred);wait_for 是通过 wait_until 实现的,到达指定截止时间或满足条件 conditions 时线程即被唤醒。不同的是,到达时间点是自动唤醒,而条件满足 conditions 时则是通过 notify_one() 或 notify_all() 唤醒,使用的时候注意判断返回值,根据返回值确定该线程是被超时唤醒还是被其他线程 notify 正常唤醒,即状态是否为 std::cv_status::timeout。notify:notify_one 唤醒一个阻塞的线程,线程唤醒的顺序可能是随机的,并没有特定的顺序。notify_one 程序示例如下,我们可以看到每次唤醒的顺序并不是确定的:C++// condition_variable::notify_one #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variable std::mutex mtx; std::condition_variable produce,consume; int cargo = 0; // shared value by producers and consumers void consumer () { std::unique_lock<std::mutex> lck(mtx); while (cargo==0) consume.wait(lck); std::cout << cargo << '\n'; cargo=0; produce.notify_one(); } void producer (int id) { std::unique_lock<std::mutex> lck(mtx); while (cargo!=0) produce.wait(lck); cargo = id; consume.notify_one(); } int main () { std::thread consumers[10],producers[10]; // spawn 10 consumers and 10 producers: for (int i=0; i<10; ++i) { consumers[i] = std::thread(consumer); producers[i] = std::thread(producer,i+1); } // join them back: for (int i=0; i<10; ++i) { producers[i].join(); consumers[i].join(); } return 0; }notify_all 唤醒所有的阻塞在 condition_variable 下的线程。需要特别注意的是,可能唤醒所有阻塞的线程,但是唤醒的多个线程同一时间只能有一个线程可以持有 lck 锁,必须等待其执行 wait 之后的代码,并释放 lck 时,剩余被唤醒的线程才可以执行,我们可以观察到以下程序示例,唤醒的顺序都是随机的。C++#include <iostream> #include <condition_variable> #include <thread> #include <chrono> std::condition_variable_any cv; std::mutex cv_m; // This mutex is used for three purposes: // 1) to synchronize accesses to i // 2) to synchronize accesses to std::cerr // 3) for the condition variable cv int i = 0; void waits(int idx) { std::unique_lock<std::mutex> lk(cv_m); std::cerr << "Waiting... \n"; cv.wait(lk, []{return i == 1;}); std::cerr << "thread:"<< idx <<" ...finished waiting. i == 1\n"; } void signals() { std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guard<std::mutex> lk(cv_m); std::cerr << "Notifying...\n"; } cv.notify_all(); std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guard<std::mutex> lk(cv_m); i = 1; std::cerr << "Notifying again...\n"; } cv.notify_all(); } int main() { std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals); t1.join(); t2.join(); t3.join(); t4.join(); }2.虚假唤醒以及如何避免虚假唤醒:当线程从等待已发出信号的条件变量中醒来,却发现它等待的条件未得到满足时,就会发生虚假唤醒。发生虚假唤醒通常是因为在发出条件变量信号和等待线程最终运行之间,另一个线程运行并更改了条件,导致 wait 的线程被唤醒后,实际条件却未满足。比如我们在 notify_all() 时多个线程都被唤醒,但此时实际共享区却只有少数几个线程可以操作,这时就会造成其他线程被虚假唤醒,可以在 wait 唤醒后再次进行检测 condition 解决虚假唤醒。虚假唤醒导致的后果:之前要等待的某个条件实际上并没有符合,被唤醒的线程将可能会执行一系列非法操作。通常的解决办法:在条件变量阻塞的代码处增加一个 while 循环,如果被唤醒就要检查一下条件是否符合,如果不符合则要再次进入阻塞等待。这样即避免了忙等待,又避免了虚假唤醒问题。C++lock(mutex); while( something not true ) { condition_wait( cond, mutex); } do(something); unlock(mutex);

C++
0
0
0
浏览量2153
silennn

请教一下c语言数组问题?是什么问题导致程序一会能行一会不行?

需求:实现将数组中所有元素调整为左右两部分,左边为奇数,右边为偶数。(c语言) int main(){ int size; printf("enter the size of arr:"); scanf("%d",&size); int arr[size]; for(int i;i

c c++ c#
12
1
0
浏览量434