一、线程的基本概念
- 定义:线程是轻量级的进程,可实现多任务的并发。
- 核心定位:进程是操作系统资源分配的最小单位。线程是操作系统任务调度的最小单位。
二、线程的创建
- 创建主体:线程由某个进程创建。
- 资源分配:进程创建线程时,会为其分配独立的8M栈区空间。线程和所在进程及进程中的其他线程,共用进程的堆区、数据区、文本区。
三、线程的调度
- 线程调度呈现“宏观并行,微观串行”的特点,由操作系统负责调度。
四、线程的消亡
- 线程退出:在_thread任务函数中使用return结束线程。调用pthread_exit(NULL)退出线程。
- 回收线程资源空间:主要通过pthread_join(tid, NULL)实现。
五、进程和线程的区别
对比维度 | 进程 | 线程 |
---|---|---|
核心定位 | 操作系统资源分配的最小单位 | 操作系统任务调度的最小单位 |
资源消耗 | 开销大,每次创建需要0-4G虚拟内存空间 | 开销较小,只需所在进程开辟8M栈区空间 |
效率角度 | 由操作系统创建,创建耗时比线程大;跨进程调度比跨线程调度慢 | 由所在进程创建;跨进程调度比跨线程调度慢 |
通信方面 | 不能直接通信,需使用进程间通信机制(IPC机制) | 通信简单,可使用线程共享区域(如全局变量) |
安全性角度 | 安全性高,各进程空间独立 | 安全性不如进程,一个线程异常可能影响同一进程中所有线程 |
六、线程的相关编程
- 线程创建:
- 函数:pthread_create(),成功返回0,失败返回非0。
- 参数包括保存线程ID的变量地址、线程属性对象地址(NULL为默认属性)、线程任务函数指针、传递给任务函数的参数。
- pthread_self()可获取当前线程的ID号。
- 线程退出:
- pthread_exit(void *retval),retval为向回收线程传递参数的地址,NULL表示不传递参数。
- 线程回收:
- 函数:pthread_join(pthread_t thread, void **retval),功能是阻塞等待回收线程资源空间,成功返回0,失败返回-1。
- 参数thread为要回收的线程ID,retval用于保存线程退出时传递的参数,NULL表示不接收。
- 线程回收策略:
- 分离属性的线程:不需要回收,由操作系统回收(适用于无空闲线程帮忙回收时)。
- 非分离属性的线程:通过pthread_join()阻塞回收。
- 线程属性:
- 分离属性:不需要被其他线程回收,会被操作系统回收,可通过pthread_detach(pthread_t thread)设置。
- 非分离属性:可被其他线程回收或结束,为默认属性。
七、线程间通信与互斥机制
- 线程间通信:可通过全局变量等线程共享区域进行通信。
- 线程间互斥机制:
- 临界资源:多个线程可同时访问的资源,如全局变量、共享内存区域等,访问时存在资源竞争问题。
- 解决资源竞争的方法:采用互斥机制,即多个线程访问临界资源时具有排他性,一次只允许一个线程访问,通过互斥锁实现。
- 互斥锁实现步骤:
- 创建互斥锁:pthread_mutex_t。
- 初始化互斥锁:pthread_mutex_init(),参数包括锁对象地址和锁的属性(NULL为默认属性),成功返回0,失败返回-1。
- 加锁:int pthread_mutex_lock(pthread_mutex_t *mutex)。
- 解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex)。
- 销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex)。
代码训练:
线程创建
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//次线程
void *task(void *arg)
{
printf("I am thread : tid = %ld\n", pthread_self());
}
int main(int argc, const char *argv[])
{
//主进程、主线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, task, NULL);
if (ret !=0)
{
printf("pthread_create error\n");
return -1;
}
while (1)
{}
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//次线程
void *task(void *arg)
{
int i = 0;
while (1)
{
if (++i == 10)
{
char str[] = {"thread over!"};
// return "hello";
pthread_exit(NULL); // return NULL;
}
printf("I am thread : tid = %ld\n", pthread_self());
sleep(1);
}
printf("hello world\n");
return NULL;
}
int main(int argc, const char *argv[])
{
//主进程、主线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, task, NULL);
if (ret !=0)
{
printf("pthread_create error\n");
return -1;
}
//void *retval;
pthread_join(tid, NULL);
//printf("retval -> %s\n", (char *)retval);
return 0;
}
#include <stdio.h>
#include <pthread.h>
int num_g = 100;
void *task1(void *arg)
{
num_g = 1000;
while (1)
{
printf("th1 -> num = %d\n", num_g);
sleep(1);
}
}
void *task2(void *arg)
{
while (1)
{
printf("th2 -> num = %d\n", num_g);
sleep(1);
}
}
int main(int argc, const char *argv[])
{
pthread_t tid[2];
pthread_create(&tid[0], NULL, task1, NULL);
pthread_create(&tid[1], NULL, task2, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
return 0;
}
detach.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//次线程
void *task(void *arg)
{
// printf("I am thread : tid = %ld\n", pthread_self());
}
int main(int argc, const char *argv[])
{
//主进程、主线程
pthread_t tid;
int i = 0;
while (1)
{
int ret = pthread_create(&tid, NULL, task, NULL);
if (ret !=0)
{
printf("pthread_create error\n");
return -1;
}
pthread_detach(tid);
i++;
printf("i = %d\n", i);
}
return 0;
}
#include <stdio.h>
#include <pthread.h>
int num_g = 0;
pthread_mutex_t mutex;
void *task1(void *arg)
{
for (int i = 0; i < 100000; i++)
{
pthread_mutex_lock(&mutex);
num_g = num_g+1;
printf("num_g = %d\n", num_g);
pthread_mutex_unlock(&mutex);
}
}
void *task2(void *arg)
{
for (int i = 0; i < 100000; i++)
{
pthread_mutex_lock(&mutex);
num_g = num_g+1;
printf("num_g = %d\n", num_g);
pthread_mutex_unlock(&mutex);
}
}
int main(int argc, const char *argv[])
{
pthread_t tid[2];
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid[0], NULL, task1, NULL);
pthread_create(&tid[1], NULL, task2, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
补充:
函数指针:
1. 定义:
返回值类型 (*指针名称)(形参表);
void *(*pfun)(void *);
2. 函数指针初始化
返回值类型 (*指针名称)(形参表)=函数的入口地址;
void *(*pfun)(void *) = main_ctl;
3. 函数指针赋值:
函数指针名称=函数的入口地址;
void *(*pfun)(void *) = NULL;
pfun = main_ctl;
4. 函数指针怎么使用
函数名(实参表);
函数指针(实参表);
5. 函数指针的数组:保存多个函数指针
返回值类型 (*数组名称[n])(形参表);
void *(*pfun[5])(void *);