C++11 线程管理

1 线程启动
2 参数传递
  2.1 参数传递
  2.2 引用传参
3 线程所有权管理
4 线程标志

1 线程启动

std::thread构造函数接受可调用对象启动线程,如下所示:

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

void f(int i) {
    std::cout << "Func, i=" << i << "\n";
}

struct Foo {
    void operator()(int i) {
        std::cout << "Class Function Call Object, i=" << i << "\n";
    }
};

struct Foo2 {
    void func(int i) {
        std::cout << "Class Method, i=" << i << "\n";
    }
};

int main() {
    // by normal function
    std::thread t1(f, 1);
    t1.join();

    // by class operator()
    Foo foo;
    std::thread t2(foo, 2);
    t2.join();

    //by lamda
    int i = 3;
    std::thread t3([i]() {
        std::cout << "Lamda, i=" << i << "\n";
    });
    t3.join();

    // by member function
    Foo2 foo2;
    auto f3 = std::mem_fn(&Foo2::func);
    std::thread t4(f3, foo2, 4);
    t4.join();

    // by bind
    auto f1 = std::bind(f, std::placeholders::_1);
    std::thread t5(f1, 5);
    t5.join();

    // by std::function
    std::function<void(int)> f2 = f;
    std::thread t6(f2, 6);
    t6.join();
}

假设文件名为thread.cpp, 则编译命令为:
g++ -std=c++11 -lpthread thread.cpp -o test_thread
执行./test_thread后程序输出为

Func, i=1
Class Function Call Object, i=2
Lamda, i=3
Class Method, i=4
Func, i=5
Func, i=6

2 参数传递

2.1 基本范例

下面例子是一个向线程传递参数的示例

#include <iostream>
#include <thread>
#include <string>
void f(int i, const std::string& s) {
    std::cout << "i = " << i << "\n"
        << "s = " << s << std::endl;
}

int main() {
    char buffer[1024];
    sprintf(buffer, "%i", 1234);
    std::thread t(f, 3, buffer);
    t.detach();
}

这个程序存在一个隐患, 由于主进程detach了,所以buffer在转成std::string之前有可能因为主进程退出而销毁,正确的方式是在主进程中就把buffer转成std::string, 如下

// std::thread t(f, 3, buffer);  =>
std::thread t(f, 3, std::string(buffer));

2.2 引用传参

C++11 支持使用std::ref向线程按照引用方式传递参数, 如下范例:

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

void func(int& i, std::string& s) {
    ++i;
    s = "update value";
}

int main() {
    int i = 0;
    std::string s("old value");
    std::thread t(func, std::ref(i), std::ref(s));
    t.join();

    std::cout << "i=" << i << "\n"
        << "s=" << s << std::endl;
    return 0;
}

程序输出结果为:

i=1
s=update value

3 线程所有权管理

std::thread都是可移动,但不可拷贝, 如下面示例:

#include <iostream>
#include <thread>

void f(int i) {
    std::cout << "Func, i=" << i << "\n";
}

std::thread gen_a_thread() {
    std::thread t(f, 2);
    return t;
}

int main() {
    std::thread t1(f, 1);

    //std::thread t2 = t1;  this is not allowed

    // transfer ownership, call move constructor
    std::thread t2 = std::move(t1);
    t2.join();

    // transfer ownership, call move constructor
    std::thread t3 = gen_a_thread();
    t3.join();
}

输出为:

Func, i=1
Func, i=2

对于需要join的线程,为了确保join一定会被调用,可以创建下面的thread_scope类,如下:

#include <iostream>
#include <thread>
#include <exception>

void f(int i) {
    std::cout << "Func, i=" << i << "\n";
}

class ThreadScope {
public:
    explicit ThreadScope(std::thread t): _t(std::move(t)) {
        if (!_t.joinable()) {
            throw std::logic_error("no thread");
        }
    }

    ~ThreadScope() {
        _t.join();
    }

    //disallow copy and assign
    ThreadScope(const ThreadScope& t) = delete;
    ThreadScope& operator=(const ThreadScope& t) = delete;

private:
    std::thread _t;
};


int main() {
    ThreadScope ts(std::thread(f, 1));
}

可以将std::thread放入std::vector中,批量创建线程并且等待它们结束,示例如下:

#include <iostream>
#include <thread>
#include <vector>
#include <sstream>
#include <algorithm>

void worker(int i) {
    std::stringstream ss;
    ss << "worker_";
    ss << i;
    ss << "\n";
    std::cout << ss.str();
}

int main() {
    const size_t WORKER_NUM = 20;
    std::vector<std::thread> threads;
    for (auto i = 0; i < WORKER_NUM; ++i) {
        threads.emplace_back(std::thread(worker, i));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
}

4 线程标志

线程标识类型是std::thread::id,可以通过两种方式进行检索。第一种,可以通过调用std::thread对象的成员函数get_id()来直接获取。如果std::thread对象没有与任何执行线程相关联,get_id()将返回std::thread::type默认构造值,这个值表示“没有线程”。第二种,当前线程中调用std::this_thread::get_id()。 示例代码如下:

#include <iostream>
#include <thread>
#include <vector>
#include <sstream>
#include <algorithm>

void worker(int i) {
    std::stringstream ss;
    ss << std::this_thread::get_id() << "\n";
    std::cout << ss.str();
}

int main() {
    const size_t WORKER_NUM = 5;
    std::vector<std::thread> threads;
    for (auto i = 0; i < WORKER_NUM; ++i) {
        threads.emplace_back(std::thread(worker, i));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
}

输出如下:

139865582319360
139865571829504
139865561339648
139865540359936
139865550849792
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,809评论 1 304
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,651评论 1 257
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,178评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,241评论 0 181
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,047评论 1 259
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,899评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,503评论 2 274
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,249评论 0 168
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,125评论 6 235
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,605评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,368评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,723评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,285评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,190评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,634评论 3 209
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,651评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,052评论 0 167
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,638评论 2 232
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,760评论 2 237

推荐阅读更多精彩内容