C ++中线程的简单示例


391

有人可以发布一个简单的示例来说明如何在C ++中启动两个(面向对象)线程。

我正在寻找可以扩展运行方法(或类似方法)的实际C ++线程对象,而不是调用C样式线程库。

我遗漏了所有特定于OS的请求,希望任何答复的人都可以使用跨平台库进行答复。我现在将其明确。


Answers:


560

创建一个您希望线程执行的函数,例如:

void task1(std::string msg)
{
    std::cout << "task1 says: " << msg;
}

现在,创建thread最终将调用上述功能的对象,如下所示:

std::thread t1(task1, "Hello");

(您需要#include <thread>访问std::thread课程)

构造函数的参数是线程将执行的函数,后跟函数的参数。线程在构建时自动启动。

如果以后要等待线程完成执行该函数,请调用:

t1.join(); 

(加入意味着调用新线程的线程将在新线程继续执行之前,等待新线程完成执行)。


编码

#include <string>
#include <iostream>
#include <thread>

using namespace std;

// The function we want to execute on the new thread.
void task1(string msg)
{
    cout << "task1 says: " << msg;
}

int main()
{
    // Constructs the new thread and runs it. Does not block execution.
    thread t1(task1, "Hello");

    // Do other things...

    // Makes the main thread wait for the new thread to finish execution, therefore blocks its own execution.
    t1.join();
}

有关std :: thread的更多信息,请点击此处

  • 在GCC上,使用编译-std=c++0x -pthread
  • 只要您的编译器支持此(C ++ 11)功能,这对任何操作系统都适用。

4
@ Preza8 VS10在C ++ 11中不支持许多功能。VS12和VS13支持线程。
jodag 2014年

3
我认为在注释“低于4.7的GCC版本中,使用-std=c++0x(而不是-std=c++0x)”,我相信第二个“ c ++ 0x”应该改为“ c ++ 11”,但这不能更改,因为编辑太小。
zentrunix 2014年

1
@MasterMastic如果我们使用std :: thread t1(&task1,“ Hello”)而不是std :: thread t1(task1,“ Hello”)传递函数的引用,会有什么区别?在这种情况下,我们应该将函数声明为指针吗?
user2452253 2014年

3
@ user2452253如果传递“ task1”,则传递一个指向要执行的函数的指针(这很好)。如果传递“&task1”,则将指针传递给函数的指针,结果可能不会那么漂亮且非常不确定(这就像试图一次又一次地执行随机指令。以正确的概率,您可能会打开通往地狱的门户)。您确实只想传递函数指针(带有函数开始地址的值的指针)。如果那不能解决问题,请告诉我,祝您好运!
2014年

2
@curiousguy好吧,正确的方法是传递要运行的函数的指针。在这种情况下,函数为task1。因此,函数指针也由表示task1。但是,感谢你提出这个问题,因为我相信我是错的,这是相当于&task1,所以不要紧形成你选择写(如果是的话,我想,函数指针的指针&&task1- 将是坏)。相关问题
MasterMastic '18年

80

嗯,从技术上讲,任何此类对象最终都将在C风格的线程库上构建,因为C ++只是std::thread在c ++ 0x中指定了一个库存模型,而该模型只是被钉牢,尚未实现。这个问题在某种程度上是系统性的,从技术上讲,现有的c ++内存模型不够严格,无法为所有“发生之前”的情况提供明确定义的语义。汉斯·勃姆(Hans Boehm)不久前就该主题撰写了一篇论文,并且在制定有关该主题的c ++ 0x标准方面发挥了作用。

http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html

那就是说,有几个跨平台的线程C ++库在实践中工作得很好。英特尔线程构建块包含一个tbb :: thread对象,该对象非常接近c ++ 0x标准,Boost具有一个boost :: thread库,其功能相同。

http://www.threadingbuildingblocks.org/

http://www.boost.org/doc/libs/1_37_0/doc/html/thread.html

使用boost :: thread您将得到类似以下内容的信息:

#include <boost/thread.hpp>

void task1() { 
    // do stuff
}

void task2() { 
    // do stuff
}

int main (int argc, char ** argv) {
    using namespace boost; 
    thread thread_1 = thread(task1);
    thread thread_2 = thread(task2);

    // do other stuff
    thread_2.join();
    thread_1.join();
    return 0;
}

8
Boost线程很棒-我唯一的问题是您(当我上次使用它时)不能访问本地基础线程句柄,因为它是私有类成员!win32中有很多东西需要您的线程句柄,因此我们对其进行了调整以使句柄公开。
Orion Edwards

4
boost :: thread的另一个问题是,我记得您没有自由设置新线程的堆栈大小-可悲的是,此功能在c ++ 0x标准中也未包括在内。
爱德华KMETT

这段代码给了我这一行的boost :: noncopyable异常:thread thread_1 = thread(task1); 可能是因为我使用的是旧版本(1.32)。
Frank

未在此范围内声明task1?
杰克·加斯顿

20

还有一个用于POSIX操作系统的POSIX库。 检查兼容性

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>

void *task(void *argument){
      char* msg;
      msg = (char*)argument;
      std::cout<<msg<<std::endl;
}

int main(){
    pthread_t thread1, thread2;
    int i1,i2;
    i1 = pthread_create( &thread1, NULL, task, (void*) "thread 1");
    i2 = pthread_create( &thread2, NULL, task, (void*) "thread 2");

    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    return 0;

}

用-lpthread编译

http://en.wikipedia.org/wiki/POSIX_Threads


18
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

void doSomething(int id) {
    cout << id << "\n";
}

/**
 * Spawns n threads
 */
void spawnThreads(int n)
{
    std::vector<thread> threads(n);
    // spawn n threads:
    for (int i = 0; i < n; i++) {
        threads[i] = thread(doSomething, i + 1);
    }

    for (auto& th : threads) {
        th.join();
    }
}

int main()
{
    spawnThreads(10);
}

13

搜索在新线程中调用其自己的实例方法的C ++类的示例时,会出现此问题,但我们无法以任何方式使用这些答案。这是一个执行此操作的示例:

class DataManager
{
public:
    bool hasData;
    void getData();
    bool dataAvailable();
};

类.cpp

#include "DataManager.h"

void DataManager::getData()
{
    // perform background data munging
    hasData = true;
    // be sure to notify on the main thread
}

bool DataManager::dataAvailable()
{
    if (hasData)
    {
        return true;
    }
    else
    {
        std::thread t(&DataManager::getData, this);
        t.detach(); // as opposed to .join, which runs on the current thread
    }
}

请注意,此示例不会进入互斥或锁定状态。


2
感谢您发布此信息。请注意,在实例方法上调用线程的一般形式如下:Foo f; std :: thread t(&Foo :: Run,&f,args ...); (其中Foo是具有“ Run()”作为成员函数的类)。
杰伊·埃尔斯顿

感谢您发布此信息。我严重不明白为什么所有线程示例都使用全局函数。
hkBattousai

9

除非有人希望在全局命名空间中使用单独的函数,否则我们可以使用lambda函数创建线程。

使用lambda创建线程的主要优点之一是,我们不需要将局部参数作为参数列表进行传递。我们可以使用相同的捕获列表,lambda的闭包属性将照顾生命周期。

这是示例代码

int main() {
    int localVariable = 100;

    thread th { [=](){
        cout<<"The Value of local variable => "<<localVariable<<endl;
    }}

    th.join();

    return 0;
}

到目前为止,我发现C ++ lambda是创建线程的最佳方法,尤其是对于简单的线程函数


8

在很大程度上取决于您决定使用的库。例如,如果使用wxWidgets库,则线程的创建将如下所示:

class RThread : public wxThread {

public:
    RThread()
        : wxThread(wxTHREAD_JOINABLE){
    }
private:
    RThread(const RThread &copy);

public:
    void *Entry(void){
        //Do...

        return 0;
    }

};

wxThread *CreateThread() {
    //Create thread
    wxThread *_hThread = new RThread();

    //Start thread
    _hThread->Create();
    _hThread->Run();

    return _hThread;
}

如果您的主线程调用CreateThread方法,则将创建一个新线程,该线程将开始执行“ Entry”方法中的代码。在大多数情况下,您必须保留对该线程的引用才能加入或停止该线程。更多信息在这里:wxThread文档


1
您忘记删除线程。也许您要创建一个分离线程?
aib

1
是的,我从当前正在处理的一些代码中提取了代码,当然,对该线程的引用存储在某个地方,以便稍后加入,停止和删除它。谢谢。:)
LorenzCK
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.