0%
2025-02-16 Compiler Explorer 使用了沙盒
OP 的程序收到了 SIGABRT 信号,但是从 Compiler Explorer 上观察到进程是因为 SIGSEGV 信号退出。原因是 Compiler Explorer 使用了沙盒,无法完全正确地反映程序因何种信号退出。
Windows 禁用系统更新
发表于:
参考 B 站教程。
在注册表中,找到:
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings
新建 DWORD 值,名称为:
flightSettingsmaxpausedays
git log --remerge-diff
发表于:
更新于:
--remerge-diff
选项在 git log
和 git show
命令中都存在。它用更容易阅读的方式显示一个合并结点相对于两个 parent 的变化。
下图是一个例子(来源在这里)。没用这个选项的时候会显示三路的差异,有三种颜色。只看绿色部分就是最终解决了冲突后的代码,但是看红色还得看红色是来自哪一个 parent,不够方便。
用了这个选项之后修改被整合在了不同的代码段中,只使用一栏标志(就不用再从左边去寻找代码来自哪个 parent,只需要看它属于哪一个代码块)。将红色的部分视为注释,只看正常颜色的字体,就可以得知冲突解决后的结果是:
libstdc++ enable_shared_from_this 源码分析
发表于:
继承 std::enable_shared_from_this
模板类之后就多了一个弱指针(_M_weak_this
)。同时还多了一个 __enable_shared_from_this_base
方法,创建共享指针时该方法能被 ADL 找到,以关联和共享控制块。该方法是私有的,不过 __shared_ptr<typename, typename>
是友元类,因此能访问它。
共享指针模板类 __shared_ptr
有个私有方法 _M_enable_shared_from_this_with
,它能通过 SFINAE 判断元素类型是否是继承自 enable_shared_from_this
的 CRTP 类,如果是则将自己的控制块关联给 enable_shared_from_this
中的弱指针。
按照这个逻辑,在创建共享指针的时候,_M_enable_shared_from_this_with
应该会被调用。这一点确实可以被验证(下面还有一些构造函数,就不展示了):
Windows 打不开服务、设备管理器等 .msc 文件
发表于:
更新于:
5.0.2 阅读 libstdc++ 中原子变量 wait 和 notify 接口
Wait 和 notify 接口介绍
std::atomic<T>::wait
用来等待原子变量值的改变,如果原子变量值和给定的参数 old 相同则阻塞,直到被 notify_all()
或者 notify_one()
通知,或者自发地解除阻塞。因此用 wait
要在循环中使用。
// std::atomic<T>::wait
void wait( T old, std::memory_order order = std::memory_order_seq_cst ) const noexcept;
void wait( T old, std::memory_order order = std::memory_order_seq_cst ) const volatile noexcept;
Cppreference 上说这个接口通常比轮询和自旋锁更高效。看了一下 libstdc++ 的实现,实际上内部也是自旋锁,只是比我们在外面用自旋锁要高效一些。实现在 libstdc++-v3/include/bits/atomic_wait.h 文件中。
wait
的实现
调用链路(同一缩进下函数的调用不区分顺序,不表示前者在后者之前调用):
__atomic_wait_address_v
__detail::__waiter<std::true_type>::_M_do_wait_v
__detail::__waiter_pool::_M_do_spin_v 🔍
// do-while loop of:
__detail::__waiter_pool::_M_do_wait 🔍
// and
__detail::_S_do_spin_v
__detail::__atomic_spin 🔍
10. Parallel algorithms
执行策略(C++17)
std::execution::seq
std::execution::par
std::execution::par_unseq
std::execution::unseq (C++20)
它们分别属于以下类型,但是使用的时候不要自己创建类型,应该直接使用标准库中提供的执行策略对象。
std::execution::sequenced_policy
std::execution::parallel_policy
std::execution::parallel_unsequenced_policy
std::execution::unsequenced_policy (C++20)
其中的并行策略仅仅是允许算法这样做,但不能强制算法按要求做。
Note that this is permission, not a requirement—the library may still execute the code on a single thread if it wishes.
11. 测试和调试多线程应用
Unwanted blocking
- Deadlock
- Livelock,和死锁的区别是在循环中积极检查条件,比如自旋锁,线程一直消耗 CPU 但始终无法前进
- Blocking on I/O or other external input,线程在等待一个不定期的、可能永远不会到来的操作
Race conditions
- Data races
- Broken invariants
- Lifetime issues
Code review
- 在并发访问下,哪些数据需要保护?
- 如何确保数据受到保护?
- 此时其他线程可能位于代码的哪个部分?
- 当前线程持有哪些互斥锁?
- 其他线程可能持有哪些互斥锁?
- 在当前线程中执行的操作与在其他线程中执行的操作之间是否存在顺序要求?如何确保这些要求得到满足?
- 当前线程加载的数据是否仍然有效?是否可能已被其他线程修改?(比如 CAS、双重校验锁)
- 如果假设其他线程可能正在修改数据,这意味着什么?如何确保这种情况永远不会发生?
Testing
书 P345 给出了一些对线程安全队列进行测试的测试点。
9. Advanced thread management
线程池
实现可以 submit 任务并获取 future 的线程池
有了 std::future
就能对提交的任务做等待。
线程池初始化时就创建指定数量的工作线程,每个线程的任务就是在循环中从线程安全队列上获取任务并运行。每个任务的类型是 std::packaged_task<result_type()> task
,每次有工作要提交都会包装到 std::packaged_task
,工作的提交者因而可以获取 std::future
。
由于 C++23 才引入 std::move_only_function<>
,书上实现了一个简单的替代。
修复 quicksort 工作线程的死锁
书上给了一个 quicksort 的例子,指出划分完成时,本线程先递归做完 new_higher
段的工作,再去等待 new_lower
段的工作的 future。这个实现中线程池中工作线程数量是固定的,如果所有工作线程都需要等待 future,那么就没有剩余的工作线程去真正推进任务了!有限线程数 + 线程等待尚未排队的任务 = 死锁。
解决方案是让线程在等待期间不要休眠,而是主动处理其他任务: