int shared_data5 = 0; std::mutex* locks5 = nullptr; int thread_size5; voidthread_function5(int id) { if (0) { // 产生死锁 // 此代码将产生一个固定数量的线程,每个线程都修改一个值,然后读取它.虽然通常一个锁就足够了,但是每个 // 线程(thread_size5)都用一个锁守卫共享数据值.每个线程都必须获得两个锁,然后才能再访问该值.如果 // 一个线程首先获得锁0,第二个线程获得锁1,那么程序将会出现死锁 if (id % 2) for (int i = 0; i < thread_size5; ++i) locks5[i].lock(); else for (int i = thread_size5; i >= 0; --i) locks5[i].lock(); shared_data5 = id; fprintf(stdout, "thread: %d, set shared value to: %d\n", id, shared_data5); if (id % 2) for (int i = thread_size5; i >= 0; --i) locks5[i].unlock(); else for (int i = 0; i < thread_size5; ++i) locks5[i].unlock(); } else { // 不会产生死锁 // 每个线程都以同一顺序获取锁,可以消除潜在的死锁.下面的程序无论创建多少线程都不会出现死锁 for (int i = 0; i < thread_size5; ++i) locks5[i].lock(); shared_data5 = id; fprintf(stdout, "thread: %d, set shared value to: %d\n", id, shared_data5); for (int i = 0; i < thread_size5; ++i) locks5[i].unlock(); } } voidtest_concurrency_deadlock() { thread_size5 = 5; std::thread* threads = new std::thread[thread_size5]; locks5 = new std::mutex[thread_size5]; for (size_t i = 0; i < thread_size5; ++i) threads[i] = std::thread(thread_function5, i); for (size_t i = 0; i < thread_size5; ++i) threads[i].join(); // test_concurrency_deadlock()继续之前,等待直到线程完成 delete[] locks5; delete[] threads; fprintf(stdout, "Done\n"); }
在同步过程中,当一个位置被读取两次,并有相同的值供读取时,就发生 ABA 的问题。然而,第二个线程已在两次读取之间执行并修改了这个值,执行其它工作,然后把值再修改回来,从而愚弄第一个线程,让它以为第二个线程尚未执行。实现无锁数据结构时,经常会遇到 ABA 问题。如果将一个条目从列表中移除,并删除,然后分配一个新的条目,并把它添加到列表中,因为优化,新的对象通常会放置在被删除的对象的相同位置。因此,指向新条目的指针可能等于旧项目的指针,这可能会导致 ABA 问题。
自旋锁(spinlock)是一种类型的锁实现,其中线程在一个循环中反复尝试获得锁,直到它终于成功。一般而言,只有当等待获得锁的时间很短时,自旋锁才是有效的。在这种情况下,自旋锁避免了昂贵的上下文切换时间和在传统的锁中等待资源时,调度运行需要花费的时间。当获得锁的等待时间是明显长时,自旋锁在试图获取一个锁时就会浪费大量的 CPU 时间。一个常见的防止自旋锁浪费 CPU 周期的缓解措施是,在 while 循环中让该线程休眠或把控制让给其它线程。