【问题标题】:Simple C++ Threading简单的 C++ 线程
【发布时间】:2010-10-10 10:21:44
【问题描述】:

我正在尝试在 C++ (Win32) 中创建一个线程来运行一个简单的方法。我是 C++ 线程的新手,但对 C# 中的线程非常熟悉。这是我正在尝试做的一些伪代码:

static void MyMethod(int data)
{
    RunStuff(data);
}

void RunStuff(int data)
{
    //long running operation here
}

我想从 MyMethod 调用 RunStuff 而不会阻塞。在单独的线程上运行 RunStuff 的最简单方法是什么?

编辑:我还应该提到我希望将依赖关系保持在最低限度。 (没有 MFC...等)

【问题讨论】:

  • 简单的 c++ 线程。那里有一个笑话。
  • LOL - 我知道,这是在问不可能。简单来说,我的意思是我不在线程之间共享任何数据,也不需要任何锁定。我只想启动线程并忘记它。

标签: c++ multithreading winapi


【解决方案1】:

可用于 Visual Studio 2013 等更新的编译器的 C++11 将线程作为语言的一部分,以及许多其他不错的部分,例如 lambdas。

包含文件threads 提供了线程类,它是一组模板。线程功能位于std:: 命名空间中。一些线程同步函数使用std::this_thread 作为命名空间(请参阅Why the std::this_thread namespace? 以获得一些解释)。

以下使用 Visual Studio 2013 的控制台应用程序示例演示了 C++11 的一些线程功能,包括使用 lambda(请参阅What is a lambda expression in C++11?)。请注意,用于线程睡眠的函数,例如std::this_thread::sleep_for(),使用来自std::chrono 的持续时间。

// threading.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

int funThread(const char *pName, const int nTimes, std::mutex *myMutex)
{
    // loop the specified number of times each time waiting a second.
    // we are using this mutex, which is shared by the threads to
    // synchronize and allow only one thread at a time to to output.
    for (int i = 0; i < nTimes; i++) {
        myMutex->lock();
        std::cout << "thread " << pName << " i = " << i << std::endl;
        // delay this thread that is running for a second.  
        // the this_thread construct allows us access to several different
        // functions such as sleep_for() and yield().  we do the sleep
        // before doing the unlock() to demo how the lock/unlock works.
        std::this_thread::sleep_for(std::chrono::seconds(1));
        myMutex->unlock();
        std::this_thread::yield();
    }

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // create a mutex which we are going to use to synchronize output
    // between the two threads.
    std::mutex  myMutex;

    // create and start two threads each with a different name and a
    // different number of iterations. we provide the mutex we are using
    // to synchronize the two threads.
    std::thread myThread1(funThread, "one", 5, &myMutex);
    std::thread myThread2(funThread, "two", 15, &myMutex);

    // wait for our two threads to finish.
    myThread1.join();
    myThread2.join();

    auto  fun = [](int x) {for (int i = 0; i < x; i++) { std::cout << "lambda thread " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } };

    // create a thread from the lambda above requesting three iterations.
    std::thread  xThread(fun, 3);
    xThread.join();
    return 0;
}

【讨论】:

    【解决方案2】:
    #include <boost/thread.hpp>
    
    static boost::thread runStuffThread;
    
    static void MyMethod(int data)
    {
        runStuffThread = boost::thread(boost::bind(RunStuff, data));
    }
    
    // elsewhere...
    runStuffThread.join(); //blocks
    

    【讨论】:

    • 如果我创建这样的线程,我是否需要担心以后释放任何内存?这样做有没有内存泄漏?
    • 这是一个使用 boost 库的示例,而不是直接使用 windows API。请参阅stackoverflow.com/questions/596360/good-c-lib-for-threading 了解有关 boost 和可能的其他库的一些信息,以简化 C++ 中的线程
    • boost::thread 使用很少的资源,所有这些资源都在应用程序退出时被清理。它也比 CreateThread() 函数或 pthread 库更容易使用。
    • 线程创建将是 C++0x 中的标准,我认为它看起来像这样。如果需要,现在使用 boost 可能会使以后更容易移植到 C++0x。
    • @Jon T - 听起来您需要一个由有限数量的工作线程组成的池,这些线程都从队列中工作。如果您根据短期需求启动无限数量的线程,由于上下文切换过多,您的程序将在重负载下表现不佳。
    【解决方案3】:

    另一种选择是pthreads - 它们适用于 Windows 和 linux!

    【讨论】:

      【解决方案4】:

      考虑使用 Win32 线程池,而不是为工作项启动新线程。启动新线程是浪费 - 每个线程默认为其堆栈保留 1 MB 的保留地址空间,运行系统的线程启动代码,导致通知传递到进程中的几乎每个 DLL,并创建另一个内核对象。线程池使您能够快速有效地为后台任务重用线程,并且会根据您提交的任务数量而增长或缩小。一般来说,考虑为永无止境的后台任务启动专用线程,并将线程池用于其他所有任务。

      在 Vista 之前,您可以使用 QueueUserWorkItem。在 Vista 上,新的线程池 API 更可靠,并提供了一些更高级的选项。每个都会导致您的后台代码开始在某个线程池线程上运行。

      // Vista
      VOID CALLBACK MyWorkerFunction(PTP_CALLBACK_INSTANCE instance, PVOID context);
      
      // Returns true on success.
      TrySubmitThreadpoolCallback(MyWorkerFunction, context, NULL);
      
      // Pre-Vista
      DWORD WINAPI MyWorkerFunction(PVOID context);
      
      // Returns true on success
      QueueUserWorkItem(MyWorkerFunction, context, WT_EXECUTEDEFAULT);
      

      【讨论】:

        【解决方案5】:

        您可以使用许多开源跨平台 C++ 线程库:

        其中有:

        Qt
        英特尔
        TBB Boost 线程

        按照你描述的方式,我认为英特尔 TBB 或 Boost 线程都可以。

        英特尔 TBB 示例:

        class RunStuff
        {
        public:
          // TBB mandates that you supply () operator  
          void operator ()()
          {
            // long running operation here
          }
        };
        
        // Here's sample code to instantiate it
        #include <tbb/tbb_thread.h> 
        
        tbb::tbb_thread my_thread(RunStuff);
        

        Boost 线程示例:
        http://www.ddj.com/cpp/211600441

        Qt 示例:
        http://doc.trolltech.com/4.4/threads-waitconditions-waitconditions-cpp.html
        (我认为这不适合您的需求,但只是为了完整性而包含在此处;您必须继承 QThread,实现 void run(),并调用 QThread: :start()):

        如果你只在Windows上编程,不关心跨平台,或许你可以直接使用Windows线程:
        http://www.codersource.net/win32_multithreading.html

        【讨论】:

          【解决方案6】:

          CreateThread (Win32)AfxBeginThread (MFC) 是两种方法。

          无论哪种方式,您的 MyMethod 签名都需要稍作更改。

          编辑:正如 cmets 和其他受访者所指出的,CreateThread 可能很糟糕。

          _beginthread and _beginthreadex 是 C 运行时库函数,根据文档相当于System::Threading::Thread::Start

          【讨论】:

          • IIRC,CreateThread API 可能是个坏主意,它不会初始化 CRT。
          • 如果动态链接到 CRT,CreateThread 会导致新线程在线程附加消息中初始化 CRT。
          【解决方案7】:

          这样安全吗:

          unsigned __stdcall myThread(void *ArgList) {
          //Do stuff here
          }
          
          _beginthread(myThread, 0, &data);
          

          在这个调用之后我需要做些什么来释放内存(比如 CloseHandle)吗?

          【讨论】:

          • 否 - 编译器会为您处理好。
          • 如果我只是创建这个线程并且它超出了范围,我将如何使用 CloseHandle?我只想开始它并忘记它。
          • 文档说如果线程函数退出,_endthread 会被自动调用
          【解决方案8】:

          如果您真的不想使用第三方库(我会推荐 boost::thread,如其他 anwsers 中所述),您需要使用 Win32API:

          static void MyMethod(int data)
          {
          
              int data = 3;
          
              HANDLE hThread = ::CreateThread(NULL,
                  0,
                  &RunStuff,
                  reinterpret_cast<LPVOID>(data),
                  0,
                  NULL);
          
          
              // you can do whatever you want here
          
              ::WaitForSingleObject(hThread, INFINITE);
              ::CloseHandle(hThread);
          }
          
          static DWORD WINAPI RunStuff(LPVOID param)
          {  
          
               int data = reinterpret_cast<int>(param);
          
               //long running operation here
          
               return 0;
          }
          

          【讨论】:

          • 这看起来非常接近我的需要,但是如果我不等待线程完成,我将如何调用 CloseHandle?
          • “你可以在这里做任何你想做的事”的评论有点误导,不是吗?对于一个实际上有机会工作的多线程程序,你应该非常小心你在那里做什么。
          • Jon T 如果您不等待线程完成,您可以立即关闭句柄:您不需要它。
          【解决方案9】:

          仅适用于 win32,无需其他库,您可以使用 创建线程函数 http://msdn.microsoft.com/en-us/library/ms682453(VS.85).aspx

          【讨论】:

            【解决方案10】:

            CreateThread (Win32) 和 AfxBeginThread (MFC) 是两种方法。

            如果您需要使用 C 运行时库 (CRT),请小心使用 _beginthread。

            【讨论】:

            【解决方案11】:

            C++ 中的简单线程是自相矛盾的!

            查看 boost 线程,了解最接近当今可用的简单方法的方法。

            对于一个最小的答案(它实际上不会为您提供同步所需的所有东西,而是从字面上回答您的问题)请参阅:

            http://msdn.microsoft.com/en-us/library/kdzttdcb(VS.80).aspx

            static 在 C++ 中也有不同的含义。

            【讨论】:

              猜你喜欢
              • 2022-01-10
              • 2011-05-09
              • 2013-11-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-17
              相关资源
              最近更新 更多