1.线程同步的三种方式
原子操作、条件变量和信号量是并发编程中用于同步和互斥的几种机制。
1.原子操作
-
原子操作是指不会被线程调度机制打断的操作,一旦开始执行,就会一直运行到结束,中间不会有任何上下文切换。原子操作通常用于实现对共享资源的无锁访问,例如,对一个计数器的自增操作。C++11引入了
std::atomic
模板类,用于支持原子类型和原子操作。 -
#include <atomic> std::atomic<int> counter(0); void incrementCounter() { ++counter; }
更多内容(原子操作,thread,function,bind 等相关资料)和资料:可以参考: C++ 11 (三)-CSDN博客
-
2.条件变量
-
条件变量是一种同步机制,允许线程等待某个条件的发生。它通常与互斥量一起使用,以确保在等待条件时对共享资源的访问是互斥的。条件变量的核心操作是
wait
、notify_one
和notify_all
。wait
操作会使线程进入等待状态,直到被notify_one
或notify_all
唤醒。示例代码:
-
#include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForSignal() { std::unique_lock<std::mutex> lck(mtx); cv.wait(lck, []{ return ready; }); // 被唤醒后执行的操作 } void sendSignal() { std::lock_guard<std::mutex> lck(mtx); ready = true; cv.notify_one(); }
3信号量:
-
信号量是一种用于控制同时访问共享资源的线程数量的同步机制。信号量有一个计数器,用于表示当前可用的资源数量。线程在访问资源前需要获取信号量,如果信号量的值大于0,则线程可以继续执行,并将信号量的值减1;如果信号量的值为0,则线程进入等待状态。C++中并没有内置的信号量类,但可以使用POSIX信号量或自定义实现。
示例代码(使用POSIX信号量):
-
#include <semaphore.h> sem_t sem; void initSemaphore() { sem_init(&sem, 0, 1); // 初始值为1,表示有一个资源可用 } void waitSemaphore() { sem_wait(&sem); // 访问资源 sem_post(&sem); }
2.题目相关
参考leetcode 1115
题目描述:
给你一个类:
class FooBar {
private:
int n;
public:
FooBar(int n) {
this->n = n;
}
void foo(function<void()> printFoo) {
for (int i = 0; i < n; i++) {
// printFoo() outputs "foo". Do not change or remove this line.
printFoo();
}
}
void bar(function<void()> printBar) {
for (int i = 0; i < n; i++) {
// printBar() outputs "bar". Do not change or remove this line.
printBar();
}
}
};
两个不同的线程将会共用一个 FooBar 实例:
线程 A 将会调用 foo() 方法,而
线程 B 将会调用 bar() 方法
请设计修改程序,以确保 "foobar" 被输出 n 次。
示例 1:
输入:n = 1
输出:"foobar"
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
示例 2:
输入:n = 2
输出:"foobarfoobar"
解释:"foobar" 将被输出两次。
*/
解答一:使用原子量。
#include<iosteam>
#include<mutex>
#include<thread>
#include<atomic>
class FooBar2{
private:
int n;
atomic<bool> flag;
public:
void printFoo(){
cout<<"Foo";
}
void printBar(){
cout<<"Bar";
}
FooBar2(int n){
this->n = n;
flag = false;
}
void foo(function<void()> printFoo){
for(int i = 0;i<n;i++){
while(flag){
this_thread::yield();
}
printFoo();
flag = true;
}
}
void bar(function<void()> printBar){
for(int i = 0;i<n;i++){
while(!flag){
this_thread::yield();
}
printBar();
flag = false;
}
}
//编译的时候需要加上-l pthread.
};
int main()
{
int n=5;
FooBar2 fb2(n);
thread t1(&FooBar2::foo,&fb2,bind(&FooBar2::printBar,&fb2));
thread t2(&FooBar2::bar,&fb2,bind(&FooBar2::printFoo,&fb2));
t1.join();
t2.join();
return 0;
}
解答二:使用条件变量+互斥锁
class FooBar
{
private:
int n;
mutex mtx;
bool flag = false;
condition_variable cv;
public:
FooBar(int n)
{
this->n = n;
}
void printFoo()
{
cout << "Foo";
}
void printBar()
{
cout << "Bar";
}
void foo(function<void()> printFoo)
{
for (int i = 0; i < n; i++)
{
unique_lock<mutex> lck(mtx);
cv.wait(lck, [this]()
{ return !this->flag; });
printFoo();
flag = true;
cv.notify_one();
}
}
void bar(function<void()> printBar)
{
for (int i = 0; i < n; i++)
{
unique_lock<mutex> lck(mtx);
cv.wait(lck, [this]()
{ return flag; });
printBar();
flag = false;
cv.notify_one();
}
}
};
解答三:使用条件变量。
需要在linux 下进行,同时编译的时候需要指明线程库
即g++ name.cpp -o name -l pthread. 才可以进行使用。
#include<semaphore.h>
class FooBar
{
private:
int n;
sem_t foo_Done;
sem_t bar_Done;
public:
FooBar(int n)
{
this->n = n;
sem_init(&foo_Done, 0, 0);
sem_init(&bar_Done, 0, 1);
}
void printFoo()
{
cout << "Foo";
}
void printBar()
{
cout << "Bar";
}
void foo(function<void()> printFoo)
{
for (int i = 0; i < n; i++)
{
sem_wait(&bar_Done);
printFoo();
sem_post(&foo_Done);
}
}
void bar(function<void()> printBar)
{
for (int i = 0; i < n; i++)
{
sem_wait(&foo_Done);
printBar();
sem_post(&bar_Done);
}
}
};
测试代码同解答一main()函数。