信号量机制
在多线程环境下,信号量机制的作用是协调各个线程,以确保它们能够正确、合理地使用公共资源。信号量是一种计数器,用于控制同时访问共享资源的线程数量。这里是信号量的一些主要作用:
- 并发控制:信号量维护一个许可集,限制了同时访问共享资源的线程数量。这有助于防止资源竞争和数据不一致的问题1。
- 同步机制:线程在访问共享资源前必须获取信号量。如果信号量的值为零,表示没有可用资源,线程将等待直到其他线程释放资源2。
- 资源管理:信号量可以作为一种资源计数器,管理有限的资源使用,如连接池、内存缓冲区等3。
- 互斥锁:当信号量的初始值设置为1时,它可以作为互斥锁使用,确保每次只有一个线程可以访问资源1。
信号量的操作通常包括两个主要动作:acquire()
(获取)和release()
(释放)。当线程完成资源的使用后,它会释放信号量,允许其他线程进入。这些操作都是原子的,确保了线程间的同步和资源的正确管理。
信号量和锁
信号量机制和锁是两种不同的同步机制,它们在多线程编程中用于不同的目的和场景。以下是它们之间的一些主要区别:
- 用途:
- 计数:
- 操作:
- 所有权:
- 类型:
总的来说,锁更多地用于保证对共享资源的独占访问,而信号量提供了更高的灵活性,可以控制多个线程对共享资源的访问。在选择使用哪种同步机制时,需要根据具体的应用场景和需求来决定。
用信号量机制来控制并发线程的退出顺序
创建了一个简单的CELLSemaphore.cpp来负责OnRun的退出在close之前
//CELLSemaphore.cpp
#ifndef _CELL_SEMAPHORE_HPP_
#define _CELL_SEMAPHORE_HPP_
#include<chrono>
#include<thread>
//信号量
class CELLSemaphore
{
public:
//阻塞当前线程
void wait()
{
_isWaitExit = true;
while(_isWaitExit) //循环等待
{
std::chrono::milliseconds t(1);
std::this_thread::sleep_for(t);
}
}
//唤醒当前线程
void wakeup()
{
_isWaitExit = false;
}
private:
bool _isWaitExit;
};
#endif // !_CELL_SEMAPHORE_HPP_
//虚假唤醒
在cellserver.cpp中的运用
private:
CELLSemaphore _sem;
void Close()
{
printf("CellServer%d.Close begin\n", _id);
if (_isRun)
{
_taskServer.Close();
_isRun = false;
_sem.wait();
}
printf("CellServer%d.Close end\n", _id);
}
void OnRun()
{
while (_isRun)
{
if (!_clientsBuff.empty())
{
std::lock_guard<std::mutex> lock(_mutex);
for (auto pClient : _clientsBuff)
{
_clients[pClient->sockfd()] = pClient;
pClient->serverId = _id;
if (_pNetEvent)
_pNetEvent->OnNetJoin(pClient);
}
_clientsBuff.clear();
_clients_change = true;
}
if (_clients.empty())
{
std::chrono::milliseconds t(1);
std::this_thread::sleep_for(t);
_oldTime = CELLTime::getNowInMilliSec();
continue;
}
fd_set fdRead;
FD_ZERO(&fdRead);
if (_clients_change)
{
_clients_change = false;
_maxSock = _clients.begin()->second->sockfd();
for (auto iter : _clients)
{
FD_SET(iter.second->sockfd(), &fdRead);
if (_maxSock < iter.second->sockfd())
{
_maxSock = iter.second->sockfd();
}
}
memcpy(&_fdRead_bak, &fdRead, sizeof(fd_set));
}
else {
memcpy(&fdRead, &_fdRead_bak, sizeof(fd_set));
}
timeval t{ 0,1 };
int ret = select(_maxSock + 1, &fdRead, nullptr, nullptr, &t);
if (ret < 0)
{
printf("selectÈÎÎñ½áÊø¡£\n");
Close();
return;
}
//else if (ret == 0)
//{
// continue;
//}
ReadData(fdRead);
CheckTime();
}
printf("CellServer%d.OnRun exit\n", _id);
ClearClients();
_sem.wakeup();
}