C++ 使用c+编写(旋转)螺纹屏障+;11原子
我试图熟悉c++11原子,因此我尝试为线程编写一个barrier类(在有人抱怨没有使用现有类之前:这更多是为了学习/自我改进,而不是出于任何实际需要)。我的课程基本上如下所示:C++ 使用c+编写(旋转)螺纹屏障+;11原子,c++,multithreading,gcc,c++11,C++,Multithreading,Gcc,C++11,我试图熟悉c++11原子,因此我尝试为线程编写一个barrier类(在有人抱怨没有使用现有类之前:这更多是为了学习/自我改进,而不是出于任何实际需要)。我的课程基本上如下所示: class barrier { private: std::atomic<int> counter[2]; std::atomic<int> lock[2]; std::atomic<int> cur_idx; int thread_count; pub
class barrier
{
private:
std::atomic<int> counter[2];
std::atomic<int> lock[2];
std::atomic<int> cur_idx;
int thread_count;
public:
//constructors...
bool wait();
};
int idx = cur_idx;
if(lock[idx] == 0)
{
__sync_val_compare_and_swap(&lock[idx], 0, 1);
}
int val = __sync_fetch_and_add(&counter[idx], 1);
if(val >= thread_count - 1)
{
__sync_synchronize();
counter[idx] = 0;
cur_idx ^= 1;
__sync_synchronize();
lock[idx] = 0;
__sync_synchronize();
return true;
}
while(lock[idx] == 1);
return false;
但是,当尝试将其用于两个线程(thread\u count
为2)时,第一个线程进入等待循环很好,但第二个线程没有解锁障碍(似乎它甚至没有到达int val=counter[idx]。fetch\u add(1);
,但我不太确定。但是,当我使用gcc原子内部函数时,使用volatile int
而不是std::atomic
并编写wait
,如下所示:
class barrier
{
private:
std::atomic<int> counter[2];
std::atomic<int> lock[2];
std::atomic<int> cur_idx;
int thread_count;
public:
//constructors...
bool wait();
};
int idx = cur_idx;
if(lock[idx] == 0)
{
__sync_val_compare_and_swap(&lock[idx], 0, 1);
}
int val = __sync_fetch_and_add(&counter[idx], 1);
if(val >= thread_count - 1)
{
__sync_synchronize();
counter[idx] = 0;
cur_idx ^= 1;
__sync_synchronize();
lock[idx] = 0;
__sync_synchronize();
return true;
}
while(lock[idx] == 1);
return false;
就我的理解而言,这两个版本之间不应该有任何根本性的区别(更重要的是,第二个版本不太可能起作用)。那么以下哪种情况适用
spin_barrier b(2);
std::thread t([&b]()->void
{
std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
b.wait();
});
b.wait();
t.join();
std::atomic_flag consumerLock;
{
// critical section
while (consumerLock.test_and_set()) { /* spin */ }
// do stuff
consumerLock.clear();
}
spin_势垒b(2);
标准::线程t([&b]()->无效
{
std::this_thread::sleep_for(std::chrono::duration(0.1));
b、 等待();
});
b、 等待();
t、 join();
因为mingw没有whave
头文件,所以我使用了一个自编版本,它基本上包装了适当的pthread函数(在有人问:是的,它可以在没有障碍的情况下工作,所以包装应该没有问题)
如有任何见解,将不胜感激
编辑:对算法进行解释以使其更清晰:
是等待屏障的线程数(因此,如果thread\u count
线程在屏障中,所有线程都可以离开屏障)thread\u count
- 当第一条(或任何一条)螺纹进入屏障时,
设置为1lock
统计屏障内的线程数,并为每个线程自动递增一次计数器
所有线程都在屏障内,因此计数器和锁重置为零如果计数器>=线程计数
- 否则,线程将等待
锁变为零
- 在下次使用屏障时,将使用不同的变量(
,计数器
),确保线程仍在等待首次使用屏障时不会出现问题(例如,当屏障解除时,线程已被抢占)锁
现在,我已经在linux下使用GCC4.5.1对它进行了测试,这两个版本似乎都工作得很好,这似乎表明mingw的
std::atomic
存在一个问题,但我仍然不完全相信,因为查看
标题发现,大多数函数只是调用适当的gcc原子,这意味着实际上不应该这样做bea两个版本之间的差异我不知道这是否会有所帮助,但以下来自Herb Sutter的并发队列实现的片段使用了基于原子的自旋锁:
std::atomic<bool> consumerLock;
{ // the critical section
while (consumerLock.exchange(true)) { } // this is the spinlock
// do something useful
consumerLock = false; // unlock
}
(如果愿意,您可以在那里使用获取和释放内存排序。)它看起来不必要的复杂。试试这个更简单的版本(好吧,我没有测试过,我只是想了想:):
#包括
类旋转障碍物
{
公众:
自旋势垒(无符号整数n):n(n),nwait(0),步骤{0}
bool wait()
{
unsigned int step=step.load();
如果(nwait_uu.fetch_uadd(1)=n_uu-1)
{
/*好的,最后一个线程*/
nwait_.store(0);//XXX:也许可以在这里使用轻松订购??
步骤提取添加(1);
返回true;
}
其他的
{
/*绕圈子跑,像小女孩一样尖叫*/
while(步骤加载()==步骤)
;
返回false;
}
}
受保护的:
/*同步线程的数量*/
常量无符号整型n_2;;
/*当前正在旋转的线程数*/
std::原子nwait_;
/*到目前为止完成的屏障同步数量,
*包起来没问题*/
std::原子阶跃;
};
编辑:
@Grizzy,我在你的第一个(C++11)版本中找不到任何错误,我也用两个线程运行了大约一亿次同步,它完成了。不过,我已经在双插槽/四核GNU/Linux机器上运行过它,所以我倾向于怀疑您的选项3。-该库(或者更确切地说,它到win32的端口)不够成熟。这里是我的一个简单版本:
// spinning_mutex.hpp
#include <atomic>
class spinning_mutex
{
private:
std::atomic<bool> lockVal;
public:
spinning_mutex() : lockVal(false) { };
void lock()
{
while(lockVal.exchange(true) );
}
void unlock()
{
lockVal.store(false);
}
bool is_locked()
{
return lockVal.load();
}
};
//spinning_mutex.hpp
#(包括示例)
#包括
#包括
#包括“spinning_mutex.hpp”
int g_i=0;
旋转互斥体g_i_互斥体;//保护g_i
无效安全增量()
{
std::锁定保护锁(g_i_互斥锁);
++g_i;
//g_i_互斥锁在锁定时自动释放
//超出范围
}
int main()
{
标准:螺纹t1(安全增量);
标准:螺纹t2(安全增量);
t1.join();
t2.连接();
}
我知道线程有点旧了,但由于它仍然是仅使用c++11搜索线程屏障时的第一个google结果,因此我想提供一个解决方案,使用std::condition\u变量来摆脱繁忙的等待。
基本上,它是chill的解决方案,但它使用的不是while
循环,而是std::conditional\u变量。wait()
和std::conditional\u变量。notify\u all()
。在我的测试中,它似乎运行良好
#include <atomic>
#include <condition_variable>
#include <mutex>
class SpinningBarrier
{
public:
SpinningBarrier (unsigned int threadCount) :
threadCnt(threadCount),
step(0),
waitCnt(0)
{}
bool wait()
{
if(waitCnt.fetch_add(1) >= threadCnt - 1)
{
std::lock_guard<std::mutex> lock(mutex);
step += 1;
condVar.notify_all();
waitCnt.store(0);
return true;
}
else
{
std::unique_lock<std::mutex> lock(mutex);
unsigned char s = step;
condVar.wait(lock, [&]{return step == s;});
return false;
}
}
private:
const unsigned int threadCnt;
unsigned char step;
std::atomic<unsigned int> waitCnt;
std::condition_variable condVar;
std::mutex mutex;
};
#包括
#包括
#包括
类自旋势垒
{
公众:
SpinningBarrier(无符号整数线程计数):
threadCnt(threadCo
#include <atomic>
#include <condition_variable>
#include <mutex>
class SpinningBarrier
{
public:
SpinningBarrier (unsigned int threadCount) :
threadCnt(threadCount),
step(0),
waitCnt(0)
{}
bool wait()
{
if(waitCnt.fetch_add(1) >= threadCnt - 1)
{
std::lock_guard<std::mutex> lock(mutex);
step += 1;
condVar.notify_all();
waitCnt.store(0);
return true;
}
else
{
std::unique_lock<std::mutex> lock(mutex);
unsigned char s = step;
condVar.wait(lock, [&]{return step == s;});
return false;
}
}
private:
const unsigned int threadCnt;
unsigned char step;
std::atomic<unsigned int> waitCnt;
std::condition_variable condVar;
std::mutex mutex;
};
struct bar_t {
unsigned const count;
std::atomic<unsigned> spaces;
std::atomic<unsigned> generation;
bar_t(unsigned count_) :
count(count_), spaces(count_), generation(0)
{}
void wait() {
unsigned const my_generation = generation;
if (!--spaces) {
spaces = count;
++generation;
} else {
while(generation == my_generation);
}
}
};
#ifndef SPINLOCK_H
#define SPINLOCK_H
#include <atomic>
#include <thread>
class SpinLock
{
public:
inline SpinLock() :
m_lock(ATOMIC_FLAG_INIT)
{
}
inline SpinLock(const SpinLock &) :
m_lock(ATOMIC_FLAG_INIT)
{
}
inline SpinLock &operator=(const SpinLock &)
{
return *this;
}
inline void lock()
{
while (true)
{
for (int32_t i = 0; i < 10000; ++i)
{
if (!m_lock.test_and_set(std::memory_order_acquire))
{
return;
}
}
std::this_thread::yield(); // A great idea that you don't see in many spinlock examples
}
}
inline bool try_lock()
{
return !m_lock.test_and_set(std::memory_order_acquire);
}
inline void unlock()
{
m_lock.clear(std::memory_order_release);
}
private:
std::atomic_flag m_lock;
};
#endif
#include <atomic>
using namespace std;
/* Fast userspace spinlock */
class spinlock {
public:
spinlock(std::atomic_flag& flag) : flag(flag) {
while (flag.test_and_set(std::memory_order_acquire)) ;
};
~spinlock() {
flag.clear(std::memory_order_release);
};
private:
std::atomic_flag& flag;
};
#include "spinlock.h"
atomic_flag kartuliga = ATOMIC_FLAG_INIT;
void mutually_exclusive_function()
{
spinlock lock(kartuliga);
/* your shared-resource-using code here */
}