全局静态变量的副作用

时间:2010-08-05 00:24:19

标签: boost

我正在编写一个UDP服务器,当前从UDP接收数据将其包装在一个对象中并将它们放入并发队列中。并发队列是此处提供的实现:http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

工作线程池将数据从队列中拉出来进行处理。

队列全局定义为:

static concurrent_queue<boost::shared_ptr<Msg> > g_work_queue_;

现在我遇到的问题是,如果我只是编写一个函数来生成数据并将其插入队列并创建一些消费者线程来将它们拉出来就可以了。 但是,当我添加基于UDP的生产者时,工作线程会停止通知队列中数据的到达。

我已将问题跟踪到concurrent_queue中push函数的末尾。 特别是行:the_condition_variable.notify_one(); 使用我的网络代码时不会返回。

所以问题与我编写网络代码的方式有关。

这就是它的样子。

enum
{
    MAX_LENGTH = 1500
};


class Msg
{
  public:
    Msg()
    {
       static int i = 0;
       i_ = i++;
       printf("Construct ObbsMsg: %d\n", i_);
    }

    ~Msg()
    {
       printf("Destruct ObbsMsg: %d\n", i_);
    }

    const char* toString() { return data_; }

  private:
    friend class server;

    udp::endpoint sender_endpoint_;
    char data_[MAX_LENGTH];
    int i_;
};

class server
{
public:
  server::server(boost::asio::io_service& io_service)
    : io_service_(io_service),
      socket_(io_service, udp::endpoint(udp::v4(), PORT))
  {
    waitForNextMessage();
  }  

  void server::waitForNextMessage()
  {
    printf("Waiting for next msg\n");

    next_msg_.reset(new Msg());

    socket_.async_receive_from(
        boost::asio::buffer(next_msg_->data_, MAX_LENGTH), sender_endpoint_,
        boost::bind(&server::handleReceiveFrom, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
  }

  void server::handleReceiveFrom(const boost::system::error_code& error, size_t bytes_recvd)
  {
    if (!error && bytes_recvd > 0) {
        printf("got data: %s. Adding to work queue\n", next_msg_->toString());
        g_work_queue.push(next_msg_); // Add received msg to work queue
        waitForNextMessage();
    } else {
        waitForNextMessage();
    } 
  }

private:
  boost::asio::io_service& io_service_;
  udp::socket socket_;

  udp::endpoint sender_endpoint_;
  boost::shared_ptr<Msg> next_msg_;
}

int main(int argc, char* argv[])
{
    try{
      boost::asio::io_service io_service;
      server s(io_service);
      io_service.run();
    catch(std::exception& e){
      std::err << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

现在我发现如果handle_receive_from能够返回,那么concurrent_queue中的notify_one()会返回。所以我认为这是因为我有一个递归循环。 那么开始收听新数据的正确方法是什么?并且异步的udp服务器示例是有缺陷的,因为我根据他们已经在做的事情。

编辑:好的问题甚至变得怪异了。

我在这里没有提到的是我有一个叫做处理器的类。 处理器看起来像这样:

class processor
{
public:
   processor::processor(int thread_pool_size) :
      thread_pool_size_(thread_pool_size) { }

  void start()
  {
    boost::thread_group threads;
    for (std::size_t i = 0; i < thread_pool_size_; ++i){
        threads.create_thread(boost::bind(&ObbsServer::worker, this));
    }
  }

  void worker()
  {
    while (true){
        boost::shared_ptr<ObbsMsg> msg;
        g_work_queue.wait_and_pop(msg);
        printf("Got msg: %s\n", msg->toString());
    }
  }

private:
  int thread_pool_size_;
};

现在看来,如果我自己提取worker函数并启动线程 从主要。有用!有人可以解释为什么一个线程的功能正如我所期望的那样在一个类之外,但是它内部有副作用吗?

EDIT2:现在它还在变得更奇怪

我拿出了两个函数(完全相同)。

一个叫消费者,另一个叫工人。

即。

void worker()
{
    while (true){
        boost::shared_ptr<ObbsMsg> msg;
        printf("waiting for msg\n");
        g_work_queue.wait_and_pop(msg);
        printf("Got msg: %s\n", msg->toString());
    }
}

void consumer()
{
    while (true){
        boost::shared_ptr<ObbsMsg> msg;
        printf("waiting for msg\n");
        g_work_queue.wait_and_pop(msg);
        printf("Got msg: %s\n", msg->toString());
    }
}

现在,消费者居住在server.cpp文件的顶部。即我们的服务器代码也存在。

另一方面,worker居住在processor.cpp文件中。

现在我根本没有使用处理器。主要功能现在看起来像这样:

void consumer();
void worker();

int main(int argc, char* argv[])
{
    try {
        boost::asio::io_service io_service;
        server net(io_service);
        //processor s(7);

        boost::thread_group threads;
        for (std::size_t i = 0; i < 7; ++i){
            threads.create_thread(worker); // this doesn't work
            // threads.create_thread(consumer); // THIS WORKS!?!?!?
        }

//        s.start();

        printf("Server Started...\n");
        boost::asio::io_service::work work(io_service);
        io_service.run();

        printf("exiting...\n");
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

为什么消费者能够接收排队的物品,但工人却不能。 它们是具有不同名称的相同实现。

这没有任何意义。有什么想法吗?

以下是接收txt“Hello World”时的示例输出:

输出1:不工作。调用worker函数或使用处理器类时。

Construct ObbsMsg: 0
waiting for msg
waiting for msg
waiting for msg
waiting for msg
waiting for msg
waiting for msg
Server Started...
waiting for msg
got data: hello world. Adding to work queue
Construct ObbsMsg: 1

输出2:在调用与工作函数相同的消费者函数时起作用。

Construct ObbsMsg: 0
waiting for msg
waiting for msg
waiting for msg
waiting for msg
waiting for msg
waiting for msg
Server Started...
waiting for msg
got data: hello world. Adding to work queue
Construct ObbsMsg: 1
Got msg: hello world <----- this is what I've been wanting to see!
Destruct ObbsMsg: 0
waiting for msg

1 个答案:

答案 0 :(得分:1)

回答我自己的问题。

似乎问题与g_work_queue的声明有关;

在头文件中声明为:static concurrent_queue&lt; boost :: shared_ptr&gt; g_work_queue;

似乎将其声明为静态不是我想做的事情。 显然,为每个编译的.o文件创建一个单独的队列对象 单独的锁等。

这解释了在同一源文件中操作队列的原因 与消费者和生产者在同一个文件中工作。 但是当它在不同的文件中时却没有,因为线程实际上在等待不同的对象。

所以我已经重新声明了工作队列。

-- workqueue.h --
extern concurrent_queue< boost::shared_ptr<Msg> > g_work_queue;

-- workqueue.cpp --
#include "workqueue.h"
concurrent_queue< boost::shared_ptr<Msg> > g_work_queue;

这样做可以解决问题。