(目录)
一、c++线程的回调函数
#include <iostream>
#include <thread>
#include <windows.h> // Linux下,头文件为 unistd.h
using namespace std;
void func(int n, const string &str) {
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
Sleep(1000); // Linux下为sleep(1)
}
}
class thread_f {
public:
void operator()(int n, const string &str) {
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
Sleep(1000); // Linux下为sleep(1)
}
}
};
class s_f {
public:
static void func(int n, const string &str) {
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
::Sleep(1000); // Linux下为sleep(1)
}
}
};
class Normal_f {
public:
void func(int n, const string &str) {
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
::Sleep(1000); // Linux下为sleep(1)
}
}
};
int main(int argc, const char **argv) {
// 普通函数回调
thread t1(func, 1, "testing(1)...");
// 使用函数对象(仿函数)创建线程
thread t2(thread_f(), 2, "testing(2)...");
// 使用lambda函数创建线程
auto f = [](int n, const string &str) {
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
Sleep(1000); // Linux下为sleep(1)
}
};
thread t3(f, 3, "testing(3)...");
// 使用静态成员函数
thread t4(s_f::func, 4, "testing(4)...");
cout << "begin" << endl;
for (int i = 0; i < 5; ++i) {
cout << "main work " << endl;
Sleep(1000); // Linux下为sleep(1)
}
// 使用普通成员函数
// 必须保证对象的生命周期比子线程要长
Normal_f n_f;
thread t5(&Normal_f::func, &n_f, 5, "testing(5)...");
cout << "end" << endl;
// 回收线程资源
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
return 0;
}
Windows下Sleep(毫秒);Linux下sleep(秒) Linux下编译: g++ -o demo demo.cpp -std=c++11 -lpthread
-
thread(const thread&)=delete; 线程类删除了拷贝构造函数,不充许线程对象之间相互拷贝。
-
thread(thread&& other) noexcept; 线程类使用移动构造函数,将线程other的资源所有权转移给新创建的线程对象。
-
赋值函数: thread& operator=(thread&& other) noexcept; thread& operator=(const other&)=delete; 线程中的资源不能被复制,如果other是右值,会进行资源所有权的转移,如果other是左值,禁止拷贝。
二、线程资源的回收
c++11已经在语言层面规范化了线程的使用。
- 线程的任务函数返回后,子线程将终止。
- 如果主程序(主线程)退出(不论是正常退出还是意外终止),全部的子线程将强行被终止。
虽然同一个进程的多个线程共享进程的栈空间,但是,每个子线程在这个栈中拥有自已私有的栈空间。所以,线程结束时需要回收资源。 回收子线程的资源有两种方法:
- 在主程序中,调用join()成员函数等待子线程退出,回收它的资源。如果子线程已退出,join()函数立即返回,否则会产生阻塞,直到子线程退出。
- 在子线程中,调用detach()成员函数分离子线程,子线程退出时,系统自动回收资源。分离后的子线程不可以再join()。
int main(int argc, const char **argv) {
thread t1(func, 1, "testing(1)...");
thread t2(thread_f(), 2, "testing(2)...");
t1.detach();
t2.detach();
cout << "begin" << endl;
for (int i = 0; i < 5; ++i) { // 主线程要最后退出
cout << "main work " << endl;
Sleep(1000);
}
cout << "end" << endl;
return 0;
}
用joinable()成员函数可以判断子线程的分离状态,函数返回布尔类型。
三、this_thread的全局函数
C++11提供了命名空间this_thread来表示当前线程,该命名空间中有四个函数:get_id()、sleep_for()、sleep_until()、yield()。
- get_id() thread::id get_id() noexcept; 该函数用于获取线程ID,thread类也有同名的成员函数。
this_thread::get_id()
t1.get_id()
- sleep_for()
this_thread::sleep_for(chrono::seconds(1));
通用性更强——在windows和linux下形式都一样。 3. sleep_until() 让线程休眠到一个时间点。 4. yield() 该函数让线程主动让出自已已经抢到的CPU时间片——礼让。
四、call_once函数
在多线程环境中,某些函数只能被调用一次,例如:初始化某个对象,而这个对象只能被初始化一次。在线程的任务函数中,可以用std::call_once()来保证某个函数只被调用一次。
头文件:#include<mutex>
#include <iostream>
#include <thread>
#include <windows.h>
#include <mutex>
using namespace std;
once_flag once; //声明全局变量once_flag,建立值为0和1的锁
void once_func(int n, const string &str) {
cout << "call once func: " << n << " " << str << endl;
}
void func(int n, const string &str) {
call_once(once, once_func, n, str);
for (int i = 1; i < 5; ++i) {
cout << "NO. " << n << " Loop: " << i << " " << str << endl;
Sleep(1000);
}
}
int main(int argc, const char **argv) {
thread t1(func, 1, "testing(1)...");
thread t2(func, 2, "testing(2)...");
t1.detach();
t2.detach();
cout << "begin" << endl;
for (int i = 0; i < 5; ++i) {
cout << "main work " << endl;
Sleep(1000);
}
cout << "end" << endl;
return 0;
}
五、native_handle函数
c++11定义了线程标准,不同的平台和编译器在实现的时候,本质上都是对操作系统的线程库进行封装,会损失一部分功能。为了弥补C++11线程库的不足,thread类提供了native_handle()成员函数,用于获得与操作系统相关的原生线程句柄,操作系统原生的线程库就可以用原生线程句柄操作线程。 linux下的例子
#include <iostream>
#include <thread>
#include <pthread.h>
using namespace std;
void func() {
for (int i = 1; i < 5; ++i) {
cout << " Loop: " << i << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
int main(int argc, const char **argv) {
thread tt(func);
this_thread::sleep_for(chrono::seconds(5));
pthread_t th_id = tt.native_handle(); // 获取操作系统原生的线程句柄
pthread_cancel(th_id);
tt.join();
return 0;
}
六、线程同步
1. 互斥锁
C++11提供了四种互斥锁:
- mutex:互斥锁。
- timed_mutex:带超时机制的互斥锁。
- recursive_mutex:递归互斥锁。
- recursive_timed_mutex:带超时机制的递归互斥锁
包含头文件:#include<mutex>
2. mutex类
- 加锁lock() 互斥锁有锁定和未锁定两种状态。如果互斥锁是未锁定状态,调用lock()成员函数的线程会得到互斥锁的所有权,并将其上锁。如果互斥锁是锁定状态,调用lock()成员函数的线程就会阻塞等待,直到互斥锁变成未锁定状态。
- 解锁unlock() 只有持有锁的线程才能解锁。
- 尝试加锁trylock() 如果互斥锁是未锁定状态,则加锁成功,函数返回true。如果互斥锁是锁定状态,则加锁失败,函数立即返回false(线程不会阻塞等待)。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex ex; // 建立互斥锁,为cout的输出建立临界区。
void func() {
for (int i = 1; i < 6; ++i) {
ex.lock();
cout << " Loop: " << i << endl;
ex.unlock();
this_thread::sleep_for(chrono::seconds(1));
}
}
int main(int argc, const char **argv) {
thread t1(func);
thread t2(func);
thread t3(func);
thread t4(func);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
3. timed_mutex类
timed_mutex tm;
tm.try_lock_for(chrono::seconds(10));
4. recursive_mutex类
递归互斥锁允许同一线程多次获得互斥锁,可以解决同一线程多次加锁造成的死锁问题。
5. lock_guard类
lock_guard是模板类,可以简化互斥锁的使用,也更安全。 lock_guard在构造函数中加锁,在析构函数中解锁。 lock_guard采用了RAII思想(在类构造函数中分配资源,在析构函数中释放资源,保证资源在离开作用域时自动释放)。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex ex; // 建立互斥锁,为cout的输出建立临界区。
void func() {
for (int i = 1; i < 6; ++i) {
lock_guard < mutex > mlock(ex);
cout << " Loop: " << i << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
int main(int argc, const char **argv) {
thread t1(func);
thread t2(func);
thread t3(func);
thread t4(func);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}