【问题标题】:boost::scoped_lock not working with local static variable?boost::scoped_lock 不适用于局部静态变量?
【发布时间】:2011-10-14 14:34:49
【问题描述】:

我制作了以下示例程序来使用 boost 线程:

#pragma once
#include "boost\thread\mutex.hpp"
#include <iostream>

class ThreadWorker
{
public:
    ThreadWorker() {}
    virtual ~ThreadWorker() {}

    static void FirstCount(int threadId)
    {
        boost::mutex::scoped_lock(mutex_);
        static int i = 0;

        for(i = 1; i <= 30; i++)
        {
            std::cout << i << ": Hi from thread:  " << threadId << std::endl;
        }

    }

private:
    boost::mutex mutex_;
};

主类:

// ThreadTest.cpp
#include "stdafx.h"
#include "boost\thread\thread.hpp"
#include "ThreadWorker.h"

int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread thread1(&ThreadWorker::FirstCount, 1);
    boost::thread thread2(&ThreadWorker::FirstCount, 2);
    boost::thread thread3(&ThreadWorker::FirstCount, 3);

    thread1.join();
    thread2.join();
    thread3.join();

    std::string input;
    std::cout << "Press <enter> to finish...\n";
    std::getline( std::cin, input );
    return 0;
}

当我运行它时,我得到以下输出:

1: Hi from thread:  1
1: Hi from thread:  3
2: Hi from thread:  3
...

看起来线程 1 先到达那里,然后是线程 3。scoped_lock 不是应该阻止其他线程进入那段代码吗?运行 FirstCount() 的第一个线程不应该完成吗?

更新

我认为我的代码有问题的是这一行:

boost::mutex::scoped_lock(mutex_);

我觉得应该是这样的:

boost::mutex::scoped_lock xyz(mutex_);

一旦我这样做,它确实会抱怨 mutex_ 不是静态的。为什么它首先起作用我不确定。将 mutex_ 更改为 static 确实会给我一个链接错误:

1>ThreadWorker.obj:错误 LNK2001:未解析的外部符号 “私有:静态类 boost::mutex ThreadWorker::mutex_” (?mutex_@ThreadWorker@@0Vmutex@boost@@A) 1>c:\something\ThreadTest\Debug\ThreadTest.exe:致命错误 LNK1120: 1 个未解决的外部问题

还在玩。

【问题讨论】:

  • 该代码无法编译。 mutex_ 是真实代码中类中的静态成员吗​​?
  • static int i 本质上是一个共享全局变量,它没有受到保护(它被不同的互斥锁保护......)
  • 静态类函数不能访问类的非静态成员。如果第一个是静态函数而第二个是非静态成员,FirstCount 无法访问 mutex_
  • 我是否这样做还有待商榷。但是,它确实封装了互斥体。
  • @curiousguy 也许他的原因是他喜欢 ThreadWorker 这个名字,所以他想创建一个对象并命名它。也许他只是在学习线程,认为它们很酷,并且正在与它们一起玩以了解它们是如何工作的。有点像你小时候在沙盒里玩耍时兴奋不已,嘴里塞了一把沙子。当你现在回头看时,你会觉得恶心和愚蠢,但在那一刻,它是一种新的、令人兴奋的东西,而且太棒了,周围没有人教你解释自己。他的问题很明确。

标签: c++ multithreading locking thread-safety boost-thread


【解决方案1】:

你有两个错误:

首先,正如已经注意到的,mutex_ 也应该是静态的:

private:
    static boost::mutex mutex_;

当然还有在某处声明它(最好是在 .cpp 文件中!):

boost::mutex ThreadWorker::mutex_{};

现在,为什么编译器不抱怨?好吧,因为您实际上并没有在这里构造带有参数mutex_ 的作用域锁:

boost::mutex::scoped_lock(mutex_);

实际上这不会调用你想要的构造函数,而是创建一个(本地)对象mutex_,它的类型为scoped_lock,由默认构造函数构造。因此,没有编译器问题。您应该将其更改为以下内容:

boost::mutex::scoped_lock l{mutex_};

现在编译器应该开始抱怨mutex_

【讨论】:

  • 这是错字吗?:boost::mutex::scoped_lock l{mutex_}; 为什么是大括号?还有为什么要在这里大括号? boost::mutex ThreadWorker::mutex_{};谢谢
  • 不是错字,这是 C++11 改进的构造函数语法,可以防止一些常见问题。我不知道 Visual Studio 是否已经支持它。
  • 有趣的是我刚试过:int(j); 它可以声明一个整数 j。我不知道那件事。 boost::mutex::scoped_lock(mutex_); 有点吓人,只是因为它会默默地接受它。
【解决方案2】:

你有三个独立的对象,它们都不能看到对方的 mutex_,因为该成员是在每个对象中创建的。

也许您也打算将 mutex_ 设为静态?

编辑:如果我将互斥锁设为静态并从 i 变量中删除静态,那么它似乎可以正常工作,就像我猜你的意思一样。似乎发生了这样的事情:每个线程立即进入循环,并且由于互斥锁不是静态的而彼此没有锁定。当他们将所有输出都输出到控制台时(我不记得在写入 cout 时是否存在互斥)并且 i 递增,他们都将静态 i 视为 30 并退出。

编辑 2:不,仍然不正确,因为在某些运行中仍然存在散布值。

编辑 3:它编译的原因是您的 scoped_lock 是一个临时的,这似乎使编译器摆脱了 mutex_ 应该是静态的事实。请尝试以下代码:

#include <iostream>
#include "boost\thread\mutex.hpp"
#include "boost\thread\thread.hpp"

class ThreadWorker
{
public:
    ThreadWorker() {}
    virtual ~ThreadWorker() {}

    static void FirstCount(int threadId)
    {
        // Created object f here rather than temprary
        boost::mutex::scoped_lock f(mutex_);
        int i = 0; // Not static

        for(i = 1; i <= 30; i++)
        {
            std::cout << i << ": Hi from thread:  " << threadId << std::endl;
        }

    }

private:
    static boost::mutex mutex_; // Static
};

// Storage for static
boost::mutex ThreadWorker::mutex_;

int main(int argc, char* argv[])
{
    boost::thread thread1(&ThreadWorker::FirstCount, 1);
    boost::thread thread2(&ThreadWorker::FirstCount, 2);
    boost::thread thread3(&ThreadWorker::FirstCount, 3);

    thread1.join();
    thread2.join();
    thread3.join();

    std::string input;
    std::cout << "Press <enter> to finish...\n";
    std::getline( std::cin, input );
    return 0;
}

【讨论】:

  • 想同样的事情,我尝试将 mutex_ 设为静态,但结果是一样的。
  • 它通过的原因不是因为编译器优化了一个临时的(如果它这样做,许多 RAII 代码会失败)。它实际上并没有构建您期望它构建的临时对象。
  • +1 @tinman:感谢您的帮助。最终,我认为 KillianDS 确定了正在发生的事情,所以我选择了他作为答案
  • @KillianDS:谢谢,不知道这是声明变量的有效语法。
【解决方案3】:

此代码是否也使用相同的编译器进行编译?

你能看出这段代码的问题吗?尝试在不编译的情况下发现问题。

C类{ 民众: 静态 int f () { 返回我; } 诠释我; }; int main() { 返回 C::f(); }

更新: 我刚刚看了你的更新。

C类{ 民众: 静态 int f () { 返回我; } 静态整数我; }; int C::i = 不管; int main() { 返回 C::f(); }

【讨论】:

    【解决方案4】:

    将我的代码更改为:

    ThreadWorker.h:

    #pragma once
    #include "boost\thread\mutex.hpp"
    #include <iostream>
    
    class ThreadWorker
    { 
    public:
        ThreadWorker();
        virtual ~ThreadWorker();
    
        static void FirstCount(int threadId);
    
    private:
        static boost::mutex mutex_;
    };
    

    ThreadWorker.cpp:

    #include "stdafx.h"
    #include "ThreadWorker.h"
    
    boost::mutex ThreadWorker::mutex_;
    
    ThreadWorker::ThreadWorker()
    {
    }
    
    ThreadWorker::~ThreadWorker()
    {
    }
    
    void ThreadWorker::FirstCount(int threadId)
    {
        boost::mutex::scoped_lock xyz(mutex_);
        static int i = 0;
    
        for(i = 1; i <= 30; i++)
        {
            std::cout << i << ": Hi from thread:  " << threadId << std::endl;
        }
    
    }
    

    ThreadTest.cpp:

    // ThreadTest.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "boost\thread\thread.hpp"
    #include "ThreadWorker.h"
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        boost::thread thread1(&ThreadWorker::FirstCount, 1);
        boost::thread thread2(&ThreadWorker::FirstCount, 2);
        boost::thread thread3(&ThreadWorker::FirstCount, 3);
    
        thread1.join();
        thread2.join();
        thread3.join();
    
        std::string input;
        std::cout << "Press <enter> to finish...\n";
        std::getline( std::cin, input );
        return 0;
    }
    

    我所做的更改是 1. 分离头文件和 cpp 文件(我最初这样做只是为了使我的帖子更紧凑) 2. 给 scoped_lock 变量一个标识符和 3. 使互斥锁成为静态的。

    它现在按预期工作,从每个线程顺序打印 30 行。仍然让我感到困惑的是为什么之前编译的代码(因为 tinman 也能够编译我的原始代码)。

    【讨论】:

    • @curiousguy:静态 int 是故意的。我想测试锁定共享资源。我不确定你对 ThreadWorker 类是什么意思。
    • @User:一旦我更改了 scoped_lock,代码就会停止编译。正如 KillianDS 指出的那样,它正在创建一个不引用类 mutex_ 成员的新局部变量。因为它从未引用类 mutex_,所以编译器不需要创建代码来从静态方法访问它,因此没有编译器错误。
    猜你喜欢
    • 2013-09-29
    • 1970-01-01
    • 2010-11-30
    • 2018-01-18
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多