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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容