目标:
- 线程的概念
- 线程和子进程的区别
- 线程的实现
- 线程同步
- 信号灯
- 互斥量和条件变量
接下来先来学习线程的简单实现
什么是线程
在一个程序里的多个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
进程是资源分配的基本单位,线程是cpu调度的基本单位
CPU调度逻辑
——以线程为单位使用随机轮片(时间片轮转)
在cpu层面去理解什么是任务,什么是逻辑,这段业务逻辑干什么,都是用线程进行计算的。就拿中关村的Intel 酷睿 i5 12400F CPU参数举例,如下图,其中六核心、十二线程,那么相乘等于72,即单位时间内可以同时跑72个线程。(所以买电脑时可以看看这些参数)。如果是一核心、一线程就代表1毫秒1个任务,即一秒钟cpu随机轮片了1000次。
所以我们平时玩电脑开了很多个应用程序,好像都在运行,其实他可能是一毫秒切换一个应用程序,因为一个进程至少都有一个执行线程,所以在调度的时候好像进程都在不断地切换,都在同时运行,因为内部是有线程的。
这里的随机分为顺序随机、时间随机、次数随机
fork和创建新线程的区别
当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID。这个新进程的运行时间是独立的,它在执行时几乎完全独立于创建它的进程
在进程里面创建一个新线程的时候,新的执行线程会拥有自己的堆栈(因此也就有自己的局部变量),但要与它的创建者共享全局变量、文件描述符、信号处理器和当前的子目录状态(即如果给一个客户端开一个线层的话,他们之前就可以共享acceptfd)
线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
线程的缺点
- 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的
- 调试一个多线程程序也比调试一个单线程程序困难得多
线程标识符
像每个进程有一个进程ID一样,每个线程也有一个线程ID
进程ID在整个系统中是唯一的,但线程不同,线程ID只在它所属的进程环境中有效
线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可以移植的操作系统不能把它作为整数处理
函数原型
pthread_create函数
作用:创建一个新的线程
原型:
参数:
thread:新线程创建成功后,保存新线程的标识符
attr:设置线程的属性,一般不需要什么特殊的属性,直接传NULL即可
start_routine: 函数指针,线程启动后要执行的函数
arg:传给线程启动函数的参数其中“void* (*start_routine)(void*) ”表示需要我们传递的一个函数地址,该函数以一个指向void的指针为参数,返回的也是一个指向void的指针。
返回值:调用成功时返回值是“0”,如果失败则返回一个错误。
示例
1、代码
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
//线程执行函数 类似run
using namespace std;
// 一个进程中全局变量在多个线程中可以共享 number++ 可以进行叠加
int number = 0;
void* pthread_func(void *p)
{
char *p1= (char*)p;
while (true)//两个while同时跑
{
number++;
cout << "子线程1号执行。。。。。。number = "<< number ++<< " p1 = " << p1 << endl;
sleep(1);
}
}
void* pthread_func2(void* p)
{
while (true)//两个while同时跑
{
int* p2 = (int*)p;
number++;
cout << "子线程2号执行。。。。。。number = " << number++ << " p2 = " <<*p2 << endl;
sleep(1);
}
}
int main()
{
pthread_t thread_id;
pthread_t thread_id2;
char buf[15] = { "hello" };
int temp = 666;
if (pthread_create(&thread_id,NULL, pthread_func, buf))
{
perror("pthread_create error");
}
if ( pthread_create(&thread_id2, NULL, pthread_func2, &temp))
{
perror("pthread_create error");
}
while (true)
{
cout << "主线程执行 number = " << number << endl;
sleep(1);
}
return 0;
}
2、运行结果
编译运行的时候会出现如下问题,这时候不是没有加上头文件,而是VS默认不支持多线程开发(一个main主线程,一个main开的子线程),所以除了引入头文件<pthread.h>,还要加上时要使用编译器命令的“-pthread”选项链接这些线程函数库(工程——》属性——》链接器——》命令行)
如果使用linux编译的话则要使用g++ main.cpp -pthread -o m
不带参数的效果:
从图中可以看出一个进程中全局变量在多个线程中可以共享 number++ 可以进行叠加
带参数的效果: