我正在使用WinSock2和WinAPI函数编写聊天。我有点麻烦。
我将std :: vector的客户端连接存储在服务器上。当新客户端连接时,新线程启动并且所有与客户端一起工作都在这个新线程中完成。我不使用类(我知道它不是很好)所以这个连接列表只是定义为全局变量。
在我看来,当几个线程同时尝试访问此列表时,可能会出现这种情况。虽然我没有注意到有任何问题,我是否需要做这样的事情:
template
class SharedVector {
std::vector vect;
CRITICAL_SECTION cs;
SharedVector(const SharedVector& rhs) {}
public:
SharedVector();
explicit SharedVector(const CRITICAL_SECTION& CS);
void PushBack(const T& value);
void PopBack();
unsigned int size();
T& operator[](int index);
virtual ~SharedVector();
};
template
SharedVector::SharedVector() {
InitializeCriticalSection(&cs);
}
template
SharedVector::SharedVector(const CRITICAL_SECTION& r): cs(r) {
InitializeCriticalSection(&cs);
}
template
void SharedVector::PushBack(const T& value) {
EnterCriticalSection(&cs);
vect.push_back(value);
LeaveCriticalSection(&cs);
}
template
void SharedVector::PopBack() {
EnterCriticalSection(&cs);
vect.pop_back();
LeaveCriticalSection(&cs);
}
那么,我的情况是否需要使用CRITICAL_SECTION而我是不是一个没有发现错误的幸运儿?
答案 0 :(得分:7)
是的,你很幸运,永远不会遇到任何问题。这是同步问题和竞争条件的问题,代码将在99.9%的情况下起作用,并且当灾难发生时您将不知道原因。
我会删除构造函数,将CRITICAL_SECTION作为参数,因为如果没有查看(可能是不存在的)文档来实现构造函数将初始化它,就不清楚了。
答案 1 :(得分:3)
此代码不是异常安全的,矢量push_back和pop_back方法可以抛出异常,并且您可能存在潜在的死锁。 这个方法:
unsigned int size();
T& operator[](int index);
也必须同步,因为它们访问数据,可以被另一个线程修改。例如,您可以访问以下数据:
value = shared_vector[shared_vector.size() - 1];
同时,另一个线程可以做到这一点:
shared_vector.PopBack();
答案 2 :(得分:3)
我遇到的最大问题是建筑。每个连接的线程是否真的需要直接访问此其他连接数组?他们真的不应该将某种抽象信息排入队列吗?如果每个连接线程都意识到它所在的宇宙的实现细节,我预计你会遇到麻烦。
但是我们假设连接线程确实需要直接访问彼此......
全球变量本身并不是邪恶的;学校只是教导它,因为它比提供细致入微的理解更容易。你必须知道全局变量的确切含义,并且假设全局是错误的选择,直到你发现你别无选择,这是一个不错的经验法则。解决许多问题的另一种方法是编写这样的函数:
SharedVector & GetSharedVector (void)
{
static SharedVector sharedVector;
return sharedVector;
}
这使您可以更好地控制类实例化的时间,而不是全局变量,如果您在具有构造函数的全局变量之间存在依赖关系,这可能是至关重要的,特别是如果这些全局变量生成线程。它还让你有机会在某人想要访问这个“全局”变量时进行调解,而不是在他们直接捅它时无力地受到影响。
许多其他想法也浮现在脑海中。没有特别的顺序...
答案 3 :(得分:1)
是的,如果你公开这样的全局向量,你肯定需要在任何读/写上锁定它。是的,这会严重损害你的表现。
此外,每个请求一个新线程通常也不是一个好主意。为什么不重新设计应用程序以使用IO完成端口呢?
答案 4 :(得分:0)
顺便说一下,将这个向量声明为全局变量并不是一个好方法。把它变成当地人会更好吗?