C++11并发与多线程

本文详细介绍了Linux系统中线程栈空间的默认大小,并探讨了多进程和多线程的并发概念。通过管道、文件、消息队列和共享内存等方法实现进程间通信,并通过socket通信技术实现不同主机上的进程通信。同时,文章深入讲解了线程的创建、结束标志以及如何使用C++11标准库创建线程,包括线程的join和detach操作。此外,还讨论了线程传参时可能遇到的问题和陷阱。
摘要由CSDN通过智能技术生成

转载并整理自 https://blog.csdn.net/qq_38231713/category_10001159.html

1 线程栈空间

我的 linux开发机上默认是8192 kbytes。

[admin@host]$ ulimit -s
8192
[admin@host]$ ulimit -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 1545653
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1048576
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 307200
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

2 并发

2.1 多进程并发

相同主机上的不同进程之间的通信方式:

  1. 管道
  2. 文件
  3. 消息队列
  4. 共享内存

不同主机上的进程的通信方式:

  1. socket通信技术

什么是管道?

管道是一种最基本的IPC(进程间通信)机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。

2.2 多线程并发

同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。

3 线程

主线程从 main() 函数开始执行,子线程也需要从一个函数开始运行(初始函数),一旦此函数运行完毕,该线程就结束了。

3.1 进程结束的标志

主线程执行完毕(子线程不论是否结束都会被终止,有例外情况)

3.2 创建线程

#include <iostream>
#include <thread>

using namespace std;

void SubThreadPrint() {
    cout << "子线程开始运行" << endl;
    cout << "子线程运行完毕" << endl;
    return;
}

int main() {
    // 创建线程
    // 执行线程
    thread my_thread(SubThreadPrint);

    // 阻塞主线程
    // 子线程执行完,主线程继续往下执行
    my_thread.join();

    // 主线程不再等待子线程
    // 子线程与主线程失去关联,驻留在后台,由C++运行时库接管
    // my_thread.detach();

    // 判断是否可以成功使用 join() 或者 detach()
    //如果返回 true,证明可以调用 join() 或者 detach()
    //如果返回 false,证明调用过 join() 或者 detach(),join() 和 detach() 都不能再调用了
    if (my_thread.joinable()) {
        cout << "可以调用可以调用join()或者detach()" << endl;
    } else {
        cout << "不能调用可以调用join()或者detach()" << endl;
    }

    cout << "Hello World!" << endl;
    return 0;
}

3.3 其他创建线程的方法

理论依据:

线程类参数是一个可调用对象。

一组可执行的语句称为可调用对象,C++中的可调用对象可以是函数、函数指针、lambda表达式、bind创建的对象或者重载了函数调用运算符的类对象。

3.3.1 创建类,重载函数调用运算符,初始化该类的对象,把该对象作为线程入口地址

#include <iostream>
#include <thread>

using namespace std;

class SubThreadClass {
public:
	// 不能带参数
	void operator()() {
		cout << "子线程开始运行" << endl;
		cout << "子线程运行完毕" << endl;
	}
};

int main() {
	SubThreadClass sub_thread_class;

	// 创建线程
	// 执行线程
	thread my_thread(sub_thread_class);

	// 阻塞主线程
	// 子线程执行完,主线程继续往下执行
	my_thread.join();

	// 主线程不再等待子线程
	// 子线程与主线程失去关联,驻留在后台,由C++运行时库接管
	// my_thread.detach();

	// 判断是否可以成功使用 join() 或者 detach()
	//如果返回 true,证明可以调用 join() 或者 detach()
	//如果返回 false,证明调用过 join() 或者 detach(),join() 和 detach() 都不能再调用了
	if (my_thread.joinable()) {
		cout << "可以调用可以调用join()或者detach()" << endl;
	}
	else {
		cout << "不能调用可以调用join()或者detach()" << endl;
	}

	cout << "Hello World!" << endl;
	return 0;
}

3.3.2 lambda 表达式创建线程

#include <iostream>
#include <thread>

using namespace std;

int main() {
	auto lambda_thread = [] {
		cout << "子线程开始执行了" << endl;
		cout << "子线程开始执行了" << endl;
	};

	// 创建线程
	// 执行线程
	thread my_thread(lambda_thread);

	// 阻塞主线程
	// 子线程执行完,主线程继续往下执行
	my_thread.join();

	// 主线程不再等待子线程
	// 子线程与主线程失去关联,驻留在后台,由C++运行时库接管
	// my_thread.detach();

	// 判断是否可以成功使用 join() 或者 detach()
	//如果返回 true,证明可以调用 join() 或者 detach()
	//如果返回 false,证明调用过 join() 或者 detach(),join() 和 detach() 都不能再调用了
	if (my_thread.joinable()) {
		cout << "可以调用可以调用join()或者detach()" << endl;
	}
	else {
		cout << "不能调用可以调用join()或者detach()" << endl;
	}

	cout << "Hello World!" << endl;
	return 0;
}

3.3.3 把某个类中的某个函数作为线程的入口地址

#include <iostream>
#include <thread>

using namespace std;

class Data_ {
public:
	void GetMsg() {
		cout << "GetMsg 子线程开始执行了" << endl;
		cout << "GetMsg 子线程开始执行了" << endl;
	}
	void SaveMsg() {
		cout << "SaveMsg 子线程开始执行了" << endl;
		cout << "SaveMsg 子线程开始执行了" << endl;
	}
};

int main() {
	Data_ data;

	// 第一个 & 是取址
	// 第二个 & 是引用
	thread my_thread1(&Data_::GetMsg, &data);
	thread my_thread2(&Data_::SaveMsg, &data);

	my_thread1.join();
	my_thread2.join();

	cout << "Hello World!" << endl;
	return 0;
}

运行结果如下

SaveMsg 子线程开始执行了
SaveMsg 子线程开始执行了
GetMsg 子线程开始执行了
GetMsg 子线程开始执行了
Hello World!

C:\Users\null\source\repos\Project1\Debug\Project1.exe (进程 14644)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

4 线程传参

4.1 传递临时对象

4.1.1 陷阱1

#include <iostream>
#include <thread>
#include <windows.h>

using namespace std;

void SubThreadFunc( int& i, char* buf) {
	// detach 之后,i 不是 var 真正的引用,实际上值传递
	// 推荐使用 const int i
	cout << i << endl;
	// buf 指向原来的字符串,不安全的
	cout << buf << endl;
}

int main() {
	int var = 1;
	char buf[] = "This is a test";

	// 函数名 + 两个参数
	thread my_thread(SubThreadFunc, var, buf);
	my_thread.join();
	// my_thread.detach();

	cout << "Hello World!" << endl;
	return 0;
}

4.1.2 陷阱2

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值