关键部分 - 是或不是?

时间:2008-12-26 18:00:37

标签: c++ winapi critical-section

我正在使用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而我是不是一个没有发现错误的幸运儿?

5 个答案:

答案 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;
}

这使您可以更好地控制类实例化的时间,而不是全局变量,如果您在具有构造函数的全局变量之间存在依赖关系,这可能是至关重要的,特别是如果这些全局变量生成线程。它还让你有机会在某人想要访问这个“全局”变量时进行调解,而不是在他们直接捅它时无力地受到影响。

许多其他想法也浮现在脑海中。没有特别的顺序...

  • 你无疑已经知道了 你在这里使用的模板语法是 奇怪的;我假设你刚输入 这段代码没有编译它。
  • 你需要一个无所事事的任务 您私人部分的运营商 防止事故(同样的 你之所以没有副本 那里的构造函数。
  • 显式复制构造函数是 在很多方面打破了。它需要 明确地复制矢量或 您将在新的SharedVector实例中使用空向量。 它应该简单地初始化它 自己的关键部分,而不是 复制它(然后初始化它)。 可能真的适合你的情况 有这个没有意义 功能完全因为网络 连接没有合理的 复制语义。
  • 您可以更轻松地使异常安全 通过创建一个类 EnteredCriticalSection的 构造函数调用 EnterCriticalSection和其中 析构函数调用 LeaveCriticalSection。 (它需要 坚持参考 关键部分,当然。)这 让它更容易安全 序列化您的其他成员 面对异常时的功能。

答案 3 :(得分:1)

是的,如果你公开这样的全局向量,你肯定需要在任何读/写上锁定它。是的,这会严重损害你的表现。

此外,每个请求一个新线程通常也不是一个好主意。为什么不重新设计应用程序以使用IO完成端口呢?

答案 4 :(得分:0)

顺便说一下,将这个向量声明为全局变量并不是一个好方法。把它变成当地人会更好吗?