提问人:Nicolas Cinq-Mars 提问时间:10/27/2023 最后编辑:Nicolas Cinq-Mars 更新时间:10/28/2023 访问量:88
如何在 C++ 中为需要访问资源池中的两个不同资源的线程管理线程同步
How to manage thread synchronization in C++ for threads which need access to two different resources from a pool of resources
问:
该程序模拟访问不同资源的多个线程。
我有一个资源池,在我的情况下是一个布尔数组: 我还创建了 6 个不同的线程,它们都需要一次访问其中两个资源:bool res[6] = {1, 1, 1, 1, 1, 1};
T1 -> needs res[0] and res[1]
T2 -> needs res[1] and res[2]
T3 -> needs res[2] and res[3]
T4 -> needs res[3] and res[4]
T5 -> needs res[4] and res[5]
T6 -> needs res[0] and res[5]
当线程使用资源时,它会将布尔值设置为 0,并在完成后恢复为 1。例如:T1 将 res[0] 和 res[1] 设置为 0,“使用它”,然后将两者设置回 1。
我不确定如何使用互斥锁或信号量来防止竞争条件。
显而易见但缓慢的方法是,当线程使用两个资源时,使用互斥锁来锁定整个部分,并在完成后将其解锁,从而防止任何其他线程占用任何资源。这样做的问题是一些线程应该能够同时运行,例如:T1 和 T3 不共享共同的资源,因此应该能够同时执行这些操作。另一方面,T1 和 T2 在共享 res[1] 时不应该同时运行。任何帮助都是值得赞赏的!
编辑:这是我尝试过为每个资源使用信号量的东西,我认为这个解决方案有效,但我可能是错的:
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <thread>
#include <vector>
#include <semaphore>
#include <chrono>
#include <syncstream>
bool el1[6]={1,1,1,1,1,1};
const int64_t tempsCreationObjetsMS = 3000;
std::binary_semaphore sem0{0}, sem1{0}, sem2{0}, sem3{0}, sem4{0}, sem5{0};
void creer_thread(std::string nom)
{
if(nom == "coupe")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[0] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 0" << std::endl;
sem0.acquire();
}
if (el1[1] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 1" << std::endl;
sem1.acquire();
}
el1[0] = 0;
el1[1] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[0] = 1;
el1[1] = 1;
sem0.release();
sem1.release();
}
else if(nom=="epee")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[1] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 1" << std::endl;
sem1.acquire();
}
if (el1[2] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 2" << std::endl;
sem2.acquire();
}
el1[1] = 0;
el1[2] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[1] = 1;
el1[2] = 1;
sem1.release();
sem2.release();
}
else if(nom=="chandelier")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[2] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 2" << std::endl;
sem2.acquire();
}
if (el1[3] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 3" << std::endl;
sem3.acquire();
}
el1[2] = 0;
el1[3] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[2] = 1;
el1[3] = 1;
sem2.release();
sem3.release();
}
else if(nom=="bague")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[3] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 3" << std::endl;
sem3.acquire();
}
if (el1[4] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 4" << std::endl;
sem4.acquire();
}
el1[3] = 0;
el1[4] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[3] = 1;
el1[4] = 1;
sem3.release();
sem4.release();
}
else if(nom=="table")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[4] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 4" << std::endl;
sem4.acquire();
}
if (el1[5] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 5" << std::endl;
sem5.acquire();
}
el1[4] = 0;
el1[5] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[4] = 1;
el1[5] = 1;
sem4.release();
sem5.release();
}
else if(nom=="porte")
{
std::osyncstream(std::cout) << "Je suis le thread " << nom << std::endl;
if (el1[0] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 0" << std::endl;
sem0.acquire();
}
if (el1[5] != 1)
{
std::osyncstream(std::cout) << nom << " en attente de la resource 5" << std::endl;
sem5.acquire();
}
el1[0] = 0;
el1[5] = 0;
std::osyncstream(std::cout) << nom << " en creation." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(tempsCreationObjetsMS));
std::osyncstream(std::cout) << nom << " cree." << std::endl;
el1[0] = 1;
el1[5] = 1;
sem0.release();
sem5.release();
}
}
int main(void)
{
std::thread th1(creer_thread,"coupe");
std::thread th2(creer_thread,"epee");
std::thread th3(creer_thread,"chandelier");
std::thread th4(creer_thread,"bague");
std::thread th5(creer_thread,"table");
std::thread th6(creer_thread,"porte");
th1.join();
th2.join();
th3.join();
th4.join();
th5.join();
th6.join();
}
答:
理想的解决方案取决于资源的性质,但可以使用信号量或互斥锁,速度更多地取决于实现。
我建议使用互斥锁,因为听起来每个资源都是唯一的,每个线程都需要等待特定的资源。每个资源都需要自己的互斥锁来控制线程对它的访问。这样,就不需要锁定其他线程,除非它们想要使用该特定资源。
例如:线程 1 正在使用资源 1,因此它将锁定资源 1 的互斥锁。如果任何其他线程想要使用资源 1,它必须检查互斥锁以查看它是否正在使用中,然后等到使用它的线程解锁互斥锁(在本例中为线程 1)。
此外,你所描述的 res boolean 理论上是一个信号量,如果你能保证对它的原子访问(即没有竞争条件)。有关原子访问的更多信息,请参阅此处。如果每个资源都不是唯一的,这意味着线程一次只需要 2 个,那么信号量方法可能更好。
我建议在尝试制作这个模拟器之前,对多线程进行更多研究,并查看现有的解决方案,例如 c++ 的 pthreads。
评论
std::atomic<bool>
std::atomic_flag
wait()
notify_one()
notify_all()