文章大图来源:pixiv_id=105375867
1 普通函数调用创建线程
1.1 基本方式
定义一个函数作为线程接口函数,例如 foo()
。我们可以通过 std::thread t(foo)
来创建线程 t
。线程 t
将执行 foo()
函数,与主线程并发执行。
线程构造函数 std::thread
需要传入的是 函数指针 ,其指向的是函数所在的地址。我们只需要传入定义的函数的名称 foo
即可。
如果函数带有参数,比如定义的函数为 void foo(int a, std::string s)
之类的,那么在传入的函数指针参数后面依次跟上对应的函数参数即可。假如我们有定义好的 int
类型变量 num
和 std::string
类型变量 str
,可以使用如下方式来创建线程:
1 std::thread t (foo, num, str) ;
当然,也可以采用临时变量,比如下面这种方式:
1 std::thread t (foo, 1 , "abc" ) ;
1.2 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> #include <thread> void foo (int a, std::string s) { std::cout << a << " " << s << "\n" ; }int main () { int num = 520 ; std::string str = "Marisa" ; std::thread t (foo, num, str) ; t.join (); std::thread t1 (foo, 521 , "Alice" ) ; t1. join (); return 0 ; }
运行结果如下:
2 lambda 表达式创建线程
2.1 基本方式
lambda 表达式是 C++ 11 引入的一个强大的特性,也即匿名函数。lambda 表达式的基本形式为 [](int a, int b, ...) {... }
,[]
部分表示捕获列表(此处捕获列表为空,如果要直接使用作用域内变量可以使用按引用捕获加上 &
等),(int a, int b, ...)
部分表示参数列表。
假如定义 lambda 表达式如下:
1 2 3 [](int a, int b){ std::cout << a << ' ' << b << "\n" ; }
使用如下方式可以使用 lambda 表达式创建线程:
1 2 3 std::thread t ([](int a, int b){ std::cout << a << ' ' << b << "\n" ; }, 1 , 2 ) ;
或者在别处定义一个 lambda 表达式 foo
,创建线程再调用该 lambda 表达式:
1 2 3 4 5 auto foo = [](int a, int b){ std::cout << a << ' ' << b << "\n" ; };std::thread t (foo, 1 , 2 ) ;
2.2 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> #include <thread> int main () { std::thread t ([](int a, int b){ std::cout << "Marisa " << a << "\n" ; std::cout << "Alice " << b << "\n" ; }, 1314 , 520 ) ; t.join (); auto foo = [](int a, int b){ std::cout << "Marisa " << a << "\n" ; std::cout << "Alice " << b << "\n" ; }; std::thread t2 (foo, 520 , 1314 ) ; t2. join (); return 0 ; }
运行结果如下:
3 仿函数(函数对象)创建线程
3.1 基本方式
定义一个类 MyClass(或者结构体),在类 MyClass 中重载 ()
运算符,使得类的对象可以像函数一样被调用,即 仿函数 。
在主函数中,创建一个 MyClass 类的对象 obj,我们可以将类对象 obj 作为线程接口函数,以 std::thread t(obj)
的形式创建线程。如果重载运算符函数有参数,加上后面再加上传递的参数即可。
3.2 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> #include <thread> class MyClass { int val;public : MyClass (int n): val (n) {} void operator () (int a) { for (int i = 1 ; i <= val; i ++ ){ std::cout << "Alice Margatroid " << a << "\n" ; } } };int main () { MyClass obj (5 ) ; std::thread t (obj, 520 ) ; t.join (); return 0 ; }
4 类成员函数创建线程
4.1 基本方式
定义一个类 MyClass,包含一个成员函数 foo()
。在创建线程时,需要传递类成员函数指针、类对象(一般会使用对象的引用)以及类成员函数所需参数作为参数。
假如创建了一个 MyClass 类对象 obj
,类中有一个成员函数为 void foo(int a)
,那么用如下形式创建线程:
1 std::thread t (&MyClass::foo, &obj, 5 ) ;
一般情况下,我们采用加上引用操作符 & 传递对象的引用,这样可以避免对象的拷贝(调用拷贝构造函数),以提升效率。也可以采用加上 std::ref
达到几乎同样的效果
&obj
本质上就是获取 obj 的地址。C++ 语法规则规定可以在使用类成员函数创建线程时,直接 加上 & 来传递成员函数指针以及对象的引用 。这和线程接口函数传递引用是不一样的(一般情况下,线程接口函数传递普通变量的引用必须用 std::ref
)。
4.2 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <thread> class MyClass { int val;public : MyClass (int n): val (n){ std::cout << "build function thread id: " << std::this_thread::get_id () << "\n" ; } MyClass (const MyClass& other){ val = other.val; std::cout << "copy build function thread id: " << std::this_thread::get_id () << "\n" ; } ~MyClass (){ std::cout << "~function thread id: " << std::this_thread::get_id () << "\n" ; } void foo (int a) { std::cout << "foo(): thread starts, thread id = " << std::this_thread::get_id () << "\n" ; for (int i = 1 ; i <= val; i ++ ){ std::cout << "Marisa " << a << "\n" ; } } };int main () { MyClass obj (5 ) ; std::thread t (&MyClass::foo, &obj, 520 ) ; t.join (); return 0 ; }
运行结果如下:
参考
std::thread cppreference