使用 atomic<int> 实现二进制信号量
Implementing Binary Semaphore using atomic<int>
我编写这段代码是为了仅使用原子来演示二进制信号量。
1 个线程生产者最初会在队列中推送 100 个元素。
稍后作为消费者的线程 2 和 3 将 运行 并行使用此队列。
问题是:我可以看到两个线程
打印相同的data/element
BinarySemaphore.cpp
std::queue<int> buffer;
int s_data = 1;
struct Semaphore
{
Semaphore():s_(1)
{
}
void wait()
{
while( s_.load() == 0); //you will keep waiting here until s_ becomes 1
s_.fetch_sub(1);
}
void signal()
{
s_.fetch_add(1);
}
private :
std::atomic<int> s_ ;
};
Semaphore s;
void producer()
{
while(s_data <= 100)
{
s.wait();
// critical section starts
{
std::ostringstream oss;
oss << "Consumer pushing data " << s_data <<endl;
cout << oss.str();
buffer.push(s_data++);
}
// critical section ends
s.signal();
}
}
void consumer()
{
while (1)
{
s.wait();
// critical section starts
if (!buffer.empty())
{
int top = buffer.front();
buffer.pop();
std::ostringstream oss;
oss << "consumer thread id= " << this_thread::get_id() << " reading data = " << top << endl;
cout << oss.str();
}
// critical section ends
s.signal();
}
}
int main()
{
Semaphore s;
std::thread prod(producer);
prod.join();
std::thread cons1(consumer);
std::thread cons2(consumer);
cons1.join();
cons2.join();
}
您在等待中有一个“空档”:
void wait()
{
while( s_.load() == 0); //you will keep waiting here until s_ becomes 1
s_.fetch_sub(1);
}
load() 和 fetch_sub 本身是原子的,但是在 while... load() 和 fetch_sub() 之间存在差距。也许你应该使用“交换”(并评估结果):https://en.cppreference.com/w/cpp/atomic/atomic/exchange or even better use compare_exchange: https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
如果您需要对原子执行不止一项操作,则需要在数据未更改的情况下检查一致性。否则你会有一个“差距”,正如其他答案中指出的那样。
有一个 compare_exchange 应该用于此:
void wait()
{
auto oldValue = s_.load();
while (oldValue == 0 || !s_.compare_exchange_strong(oldValue, oldValue - 1))
oldValue = s_.load();
}
现在,如果 oldValue
已过期,oldValue
将被更新并执行新的检查(循环的新迭代),并在下一次迭代中再次检查条件。
我编写这段代码是为了仅使用原子来演示二进制信号量。
1 个线程生产者最初会在队列中推送 100 个元素。
稍后作为消费者的线程 2 和 3 将 运行 并行使用此队列。
问题是:我可以看到两个线程
打印相同的data/elementBinarySemaphore.cpp
std::queue<int> buffer;
int s_data = 1;
struct Semaphore
{
Semaphore():s_(1)
{
}
void wait()
{
while( s_.load() == 0); //you will keep waiting here until s_ becomes 1
s_.fetch_sub(1);
}
void signal()
{
s_.fetch_add(1);
}
private :
std::atomic<int> s_ ;
};
Semaphore s;
void producer()
{
while(s_data <= 100)
{
s.wait();
// critical section starts
{
std::ostringstream oss;
oss << "Consumer pushing data " << s_data <<endl;
cout << oss.str();
buffer.push(s_data++);
}
// critical section ends
s.signal();
}
}
void consumer()
{
while (1)
{
s.wait();
// critical section starts
if (!buffer.empty())
{
int top = buffer.front();
buffer.pop();
std::ostringstream oss;
oss << "consumer thread id= " << this_thread::get_id() << " reading data = " << top << endl;
cout << oss.str();
}
// critical section ends
s.signal();
}
}
int main()
{
Semaphore s;
std::thread prod(producer);
prod.join();
std::thread cons1(consumer);
std::thread cons2(consumer);
cons1.join();
cons2.join();
}
您在等待中有一个“空档”:
void wait()
{
while( s_.load() == 0); //you will keep waiting here until s_ becomes 1
s_.fetch_sub(1);
}
load() 和 fetch_sub 本身是原子的,但是在 while... load() 和 fetch_sub() 之间存在差距。也许你应该使用“交换”(并评估结果):https://en.cppreference.com/w/cpp/atomic/atomic/exchange or even better use compare_exchange: https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
如果您需要对原子执行不止一项操作,则需要在数据未更改的情况下检查一致性。否则你会有一个“差距”,正如其他答案中指出的那样。
有一个 compare_exchange 应该用于此:
void wait()
{
auto oldValue = s_.load();
while (oldValue == 0 || !s_.compare_exchange_strong(oldValue, oldValue - 1))
oldValue = s_.load();
}
现在,如果 oldValue
已过期,oldValue
将被更新并执行新的检查(循环的新迭代),并在下一次迭代中再次检查条件。