1.创建多个线程:
创建多个线程时,一般由主线程统一创建,并等待释放资源或者分离线程,不要递归创建
- 多个线程如果任务相同,则可以使用同一个线程执行函数
- 多个线程如果任务不同,则可以使用不同的线程执行函数
示例代码:进程A和进程B执行相同的任务:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
// 线程执行函数
void* do_thread_function(void* args)
{
for(int i=0;i<5;i++)
{
printf("i=%d\n",i+1);
sleep(1);
}
pthread_exit(NULL);
}
int main()
{
pthread_t threadids[2]={0};
int result;
for(int i=0;i<2;i++)
{
int result =pthread_create(threadids+i,NULL,do_thread_function,NULL);
if(result!=0)
{ 、
fprintf(stderr,"pthreaderror:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("threadA id is %ld\n",*(threadids+i));
}
pthread_join(threadids[0],NULL);
pthread_join(threadids[1],NULL);
return 0;
}
示例代码:进程A和进程B执行不同的任务
cpp
#include <unistd.h>
// 线程执行函数
void* do_thread_funA(void* args)
{
printf("do thread A\n");
pthread_exit(NULL);
}
void* do_thread_funB(void* args)
{
printf("do thread b\n");
pthread_exit(NULL);
}
int main()
{
pthread_t thread_id_a,thread_id_b;
int result = pthread_create(&thread_id_a,NULL,do_thread_funA,NULL);
if(result!=0)
{
fprintf(stderr,"pthread error:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("threadA id is %ld\n",thread_id_a);
pthread_detach(thread_id_a);
result = pthread_create(&thread_id_b,NULL,do_thread_funB,NULL);
if(result!=0)
{
fprintf(stderr,"pthread error:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("threadB id is %ld\n",thread_id_b);
pthread_detach(thread_id_b);
while(1);
return 0;
}
2.线程的通信
为什么需要线程通信?
线程是操作系统调度的最小单元,有自己的栈空间,可以按照既定的代码逐步的执行,但是如果每个线程间都孤立的运行,那就会造资源浪费。所以在现实中,我们需要这些线程间可以按照指定的规则共同完成一件任务,所以这些线程之间就需要互相协调,这个过程被称为线程的通信。线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。
一、主线程向子线程传递参数
通过 pthread_create 函数的第4个参数 arg 进行传递
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 线程执行函数
void* do_thread_function(void* args)
{
printf("num = %d\n",*(int*)args);
pthread_exit(NULL);
}
int main()
{
pthread_t thread_id;
int num = 100;
int result =
pthread_create(&thread_id,NULL,do_thread_function,&num);
if(result!=0)
{
fprintf(stderr,"pthread error:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("thread id is %ld\n",thread_id);
pthread_join(thread_id,NULL);
return 0;
}
二、子线程给主线程传递参数
子线程给主线程传参的方式如下:
- 在子线程将需要返回的值存储在 pthread_exit 函数中的 retval 参数中
- 在主线程中通过 pthread_join 函数的第2个参数 retval 得到返回, pthread_join 函数会将线程的返回值(指针)保存到 retval 中
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 线程执行函数
void* do_thread_function(void* args)
{
static float score = 92.3;
pthread_exit(&score);
}
int main()
{
pthread_t thread_id;
int result = pthread_create(&thread_id,NULL,do_thread_function,NULL);
if(result!=0)
{
fprintf(stderr,"pthread error:%s\n",strerror(result));
exit(EXIT_FAILURE);
}
printf("thread id is %ld\n",thread_id);
void* res = NULL;
pthread_join(thread_id,&res);
printf("*res = %.2f\n",*(float*)res);
return 0;
}
3. 线程间互斥锁
一、关于线程互斥锁
线程的主要优势在于能够通过全局变量来共享信息,不过这种便捷的共享是有代价的:
- 必须确保多个线程不会同时修改同一变量
- 某一线程不会读取正由其他线程修改的变量,实际就是 不能让两个线程同时对临界区进行访问
- 线程互斥锁则可以用于解决多线程资源竞争问题
示例代码:
创建两个子线程,定义一个全局变量 global = 0,子线程对此全局变量进行加1操作
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
int global = 0;
//线程执行函数
void* do_thread(void* argv)
{
//循环对global进行加1操作
int loops = *(int*)argv;
for(int i=0;i<loops;i++)
{
int temp = global;
temp++;
global=temp;
}
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
if(argc !=2)
{
fprintf(stderr,"arguments must be 2:< cmd > <count>\n");
exit(EXIT_FAILURE);
}
// 获取循环次数
int loopCount = atoi(argv[1]);
// 循环的方式创建两个线程
pthread_t tids[2]={0};
int err;
for(int i=0;i<2;i++)
{
err = pthread_create(tids+i,NULL,do_thread,&loopCount);
if(err!=0)
{
fprintf(stderr,"pthread_create
failed:%s\n",strerror(err));
exit(EXIT_FAILURE);
}
}
pthread_join(tids[0],NULL);
pthread_join(tids[1],NULL);
// 打印全局变量
printf("global=%d\n",global);
return 0;
}
Tips:
将数字字符串转换为整数
cpp
int atoi(const char *nptr);
运行结果:
`
问题:当循环次数为100000和10000000时,打印结果出错原因:
解决方案:
使用线程互斥锁
5.线程互斥锁
- 线程互斥锁工作机制
当线程A获得锁,另外一个线程B在获得锁时则会阻塞,直到线程A释放锁,线程B才会获得锁。 - 线程互斥锁工作原理
本质上是一个pthread_mutex_t类型的变量,假设名为 v
1.当 v = 1,则表示当前临界资源可以竞争访问,得到互斥锁的线程则可以访问,此时 v = 0
2 当 v = 0,则表示临界资源正在被某个线程访问,其他线程则需要等待 - 线程互斥锁的特点
互斥锁是一个pthread_mutex_t 类型的变量,就代表一个互斥锁
如果两个线程访问的是同一个 pthread_mutex_t 变量,那么它们访问了同一个互斥锁对应的变量定义在 pthreadtypes.h 头文件中,是一个共用体中包含一个结构体
6.线程互斥锁的初始化
线程互斥锁的初始化方式主要分为两种
-
静态初始化
定义 pthread_mutex_t 类型的变量,然后对其初始化为 PTHREAD_MUTEX_INITIALIZER
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER -
动态初始化
动态初始化主要涉及两个函数 pthread_mutex_init 函数与 pthread_mutex_destroy 函数
pthread_mutex_init 函数
cpp
1.函数头文件
#include <pthread.h>
2.函数原型
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
3.函数功能
初始化线程互斥锁
4.函数参数
mutex:线程互斥锁对象指针
attr:线程互斥锁属性
5.函数返回值
成功:返回0
失败:返回错误编码
pthread_mutex_destroy 函数
cpp
1.函数头文件
#include <pthread.h>
2.函数原型
int pthread_mutex_destroy(pthread_mutex_t *mutex);
3.函数功能
销毁线程互斥锁
4.函数参数
mutex:线程互斥锁指针
5.函数返回值
成功:返回 0
失败:返回错误编码
7.线程互斥锁的操作
线程互斥锁的操作主要分为 获取锁(lock) 与 释放锁(unlock)
获取锁的函数:pthread_mutex_lock
cpp
函数头文件
#include <pthread.h>
函数原型
int pthread_mutex_lock(pthread_mutex_t *mutex);
函数功能
将互斥锁进行锁定,如果已经锁定,则阻塞线程
函数参数
mutex:线程互斥锁指针
函数返回值
成功:返回0
失败:返回错误码
释放锁的函数:pthread_mutex_unlock
cpp
函数头文件
#include <pthread.h>
函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);
函数功能
解除互斥锁锁定状态,解除后,所有线程可以重新竞争锁
函数参数
mutex:线程互斥锁对象的指针
函数返回值
成功:返回 0
失败:返回错误码
8.示例代码:
cpp
using namespace std;
#include<iostream>
#include<string>
#include<vector>
#include<deque>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<ptherad.h>
int global = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * start_routinue(void* arg){
int loop = *(int*)arg;
pthread_mutex_lock(&mutex);
for(int i = 0;i < loop;i++){
int temp = global;
temp++;
global = temp;
}
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main(int argc,char* argv[]){
if(argc < 2){
cout << "error" << endl;
exit(EXIT_FAILURE);
}
int count = atoi(argv[1]);
vector<pthread_t> arr;
arr.push_back(0);
arr.push_back(0);
for(vector<pthread_t> :: iterator it = arr.begin();it != arr.end();it++){
int ret = pthread_create(&(*it),NULL,start_routinue,&count);
if(ret != 0){
cout << "create failed" << endl;
exit(EXIT_FAILURE);
}
}
pthread_join(arr.at(0),NULL);
pthread_join(arr.at(1),NULL);
cout << global << endl;
}