摘要:
本次内容是关于Linux C下的线程基础以及多线程中相关函数的用法,并且都有一些简单的例子来帮助大家理解。
目录
1、线程基础
概念
线程在进程的空间中的一个任务,对应一个线程函数,它是线程函数的一次执行过程。
线程的共有资源和私有资源
1、对于一个进程中的多个线程,以下为线程公有资源:
可执行的指令(.text)
静态数据(.rodata,.data,.bss)
进程中打开的文件描述符
信号处理函数
当前工作目录
用户ID
用户组ID
2、线程私有资源 :
线程ID (TID)
PC(程序计数器)和相关寄存器
堆栈
局部变量
返回地址
错误号 (errno)
信号掩码和优先级
执行状态和属性
线程相关api函数(系统调用)
创建线程
#include <pthread.h>
Compile and link with -pthread.
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
//参数1 ----- 线程ID的指针
//参数2 ----- 线程的属性,默认属性:NULL
//参数3 ----- 线程要执行的函数(线程函数),必须按下面的格式定义
void* xxx(void* arg)
{
......
......
}
//参数4 ----- 传给线程的参数
//返回值 --- 成功:0,失败: 错误码
例如:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
//子线程
void* fun(void*arg)
{
int i;
for(i = 0; i < 7; i++){
printf("this is a function!\n");
sleep(1);
}
return 0;
}
int main(int argc,char **argv)
{
int i;
pthread_t tid;
//fun();
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
for(i = 0; i < 7; i++){
printf("this is a main!\n");
sleep(1);
}
return 0;
}
//给线程函数传递参数
例如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct student{
int sno;
char name[20];
float score;
};
//子线程
void* fun(void*arg)
{
struct student * p = (struct student*)arg;
printf("%d %s %.2f\n",p->sno,p->name,p->score);
//printf("%d\n",*(int*)arg);
//printf("%s\n",(char*)arg);
return 0;
}
int main(int argc,char **argv)
{
pthread_t tid;
//int a = 120;
//char str[] = "hello world";
struct student st = {1001,"jack",98.4};
//创建线程
//if(pthread_create(&tid,NULL,fun,&a)){
//if(pthread_create(&tid,NULL,fun,str)){
if(pthread_create(&tid,NULL,fun,&st)){
perror("pthread_create");
exit(1);
}
sleep(1);
return 0;
}
线程收尸
//如果线程没有结束,则会使调用的线程阻塞,直到被收尸的线程结束
int pthread_join(pthread_t thread, void **retval);
//参数1 ----- 被收尸的线程ID
//参数2 ----- 保存线程返回值的变量的地址
//返回值 ---- 成功:0,失败:错误码
例如:
struct student{
int sno;
char name[20];
float score;
};
//子线程
void* fun(void*arg)
{
static int a = 120;
struct student * p = (struct student*)arg;
printf("%d %s %.2f\n",p->sno,p->name,p->score);
//printf("%d\n",*(int*)arg);
//printf("%s\n",(char*)arg);
return &a;
}
int main(int argc,char **argv)
{
pthread_t tid;
//int a = 120;
//char str[] = "hello world";
struct student st = {1001,"jack",98.4};
//创建线程
//if(pthread_create(&tid,NULL,fun,&a)){
//if(pthread_create(&tid,NULL,fun,str)){
if(pthread_create(&tid,NULL,fun,&st)){
perror("pthread_create");
exit(1);
}
int *p;
//使主线程挂起,给子线程收尸
if(pthread_join(tid,(void**)&p)){
//if(pthread_join(tid,NULL)){
perror("pthread_join");
exit(1);
}
printf("*p = %d\n",*p);
return 0; //exit(0)
}
结束当前进程
#include <pthread.h>
void pthread_exit(void *retval);
//参数 ---- 返回给主线程的数据
例如:
//子线程
void* fun(void*arg)
{
int i;
static int a = 120;
for(i = 0; i <10; i++){
printf("我是一个子线程!\n");
sleep(1);
}
//exit(0); //结束进程
#if 0
return 0; //结束线程的执行 = pthread_exit(0);
#else
pthread_exit(&a); //结束线程的执行
#endif
}
int main(int argc,char **argv)
{
pthread_t tid;
int i;
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
int *p;
if(pthread_join(tid,(void**)&p)){
perror("pthread_join");
exit(1);
}
printf("*p = %d\n",*p);
for(i = 0; i <10; i++){
printf("我是main!\n");
sleep(1);
}
#if 0
return 0; //调用exit(0),结束进程
#else
pthread_exit(0); //结束线程,如果主线程结束了,还有其他线程,则等所有线程结束进程才会结束
#endif
}
结束另一个线程的执行
int pthread_cancel(pthread_t thread);
//参数 ------- 要结束的线程的ID
//返回值 ------ 成功:0,失败:错误码
例如:
//子线程
void* fun(void*arg)
{
int i;
for(i = 0; i <10; i++){
printf("我是一个子线程!\n");
sleep(1);
}
pthread_exit(0); //结束线程的执行
}
int main(int argc,char **argv)
{
pthread_t tid;
int i;
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
for(i = 0; i <10; i++){
printf("我是main!\n");
sleep(1);
if(i == 5)
pthread_cancel(tid); //结束子线程的执行
}
pthread_exit(0); //结束线程,如果主线程结束了,还有其他线程,则等所有线程结束进程才会结束
}
获取线程的ID
pthread_t pthread_self(void); //返回调用线程的ID
例如 :
//子线程
void* fun(void*arg)
{
printf("子线程ID:%ld\n",pthread_self());
return 0;
}
int main(int argc,char **argv)
{
pthread_t tid;
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
//使主线程挂起,给子线程收尸
if(pthread_join(tid,NULL)){
perror("pthread_join");
exit(1);
}
printf("主线程ID:%ld\n",pthread_self());
return 0;
}
设置线程的属性
当创建线程时,通常需要设置线程的属性,属性分为两种:
接合属性(默认属性): 该线程结束时,不会自己释放资源,需要被主线程回收资源
分离属性:该线程结束时,它可以自己释放资源,此时,不再需要主线程回收资源
设置线程属性有两种方法:
方法一:通过函数设置:
int pthread_detach(pthread_t thread);
//参数 ----- 线程的ID
//返回值 ---成功:0,失败:错误码
例如:
//子线程
void* fun(void*arg)
{
int i;
for( i = 0; i < 4;i++){
printf("子线程ID:%ld\n",pthread_self());
sleep(1);
}
return 0;
}
int main(int argc,char **argv)
{
pthread_t tid;
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
//设置线程属性为分离属性
if(pthread_detach(tid)){
perror("pthread_detach");
exit(1);
}
//使主线程挂起,给子线程收尸
if(pthread_join(tid,NULL)){ //当线程为分离属性时,不需要再调用pthread_join()
perror("pthread_join");
//exit(1);
}
printf("主线程ID:%ld\n",pthread_self());
pthread_exit(0);
}
方法二:在创建线程时,直接设置为分离属性
int pthread_attr_init(pthread_attr_t *attr); //初始化线程属性变量
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); //设置线程属性
//参数1 --- 线程属性变量的地址
//参数2 --- 属性:
PTHREAD_CREATE_DETACHED -----分离属性
PTHREAD_CREATE_JOINABLE -----接合属性
例如:
int main(int argc,char **argv)
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//创建线程
if(pthread_create(&tid,&attr,fun,NULL)){
//if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
//使主线程挂起,给子线程收尸
if(pthread_join(tid,NULL)){ //当线程为分离属性时,不需要再调用pthread_join()
perror("pthread_join");
//exit(1);
}
printf("主线程ID:%ld\n",pthread_self());
pthread_exit(0);
}
2、线程同步和互斥
同步----信号量
同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情
信号量代表某一类资源,其值表示系统中该资源的数量
信号量是一个受保护的变量,只能通过三种操作来访问
初始化
P操作(申请资源)
P(S) 含义如下:
if (信号量的值大于0) { 申请资源的任务继续运行;信号量的值减一;}
else { 申请资源的任务阻塞;}
V操作(释放资源)
V(S) 含义如下:
if (没有任务在等待该资源) { 信号量的值加一;}
else { 唤醒第一个等待的任务,让其继续运行}
信号量的值为非负整数
int sem_init(sem_t *sem, int pshared, unsigned int value);
//参数1 ---- 信号量的地址
//参数2 ---- 权限: 0--线程之间,非0---进程之间
//参数3 ---- 信号量的初始值
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作
例如:
sem_t sem;
char buf[100];
//子线程
void* fun1(void*arg)
{
while(1){
printf("请输入字符串:");
fgets(buf,sizeof(buf),stdin);
sem_post(&sem); //执行v操作
}
return 0;
}
void* fun2(void*arg)
{
while(1){
sem_wait(&sem); //执行p操作
printf("%s",buf);
sleep(1);
}
return 0;
}
int main(int argc,char **argv)
{
pthread_t tid1,tid2;
//初始化信号量
sem_init(&sem,0,0);
//创建线程
if(pthread_create(&tid1,NULL,fun1,NULL)){
perror("pthread_create");
exit(1);
}
if(pthread_create(&tid2,NULL,fun2,NULL)){
perror("pthread_create");
exit(1);
}
//等待线程结束
if(pthread_join(tid1,NULL)){
perror("pthread_join");
exit(1);
}
if(pthread_join(tid2,NULL)){
perror("pthread_join");
exit(1);
}
return 0;
}
线程互斥
引入互斥(mutual exclusion)锁的目的是用来保证共享数据操作的完整性。
互斥锁主要用来保护临界资源
每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源
线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止
//初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr); //动态初始化
//参数1 ----- 互斥锁的指针
//参数2 ----- 互斥锁属性,一般为NULL
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态 初始化
//上锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
例如:
pthread_mutex_t mutex;
int a,b;
//子线程
void* fun(void*arg)
{
int i;
for( i = 0; ; i++){
pthread_mutex_lock(&mutex); //上锁
a = i;
b = i;
pthread_mutex_unlock(&mutex); //解锁
}
return 0;
}
int main(int argc,char **argv)
{
pthread_t tid;
pthread_mutex_init(&mutex,NULL);
//创建线程
if(pthread_create(&tid,NULL,fun,NULL)){
perror("pthread_create");
exit(1);
}
while(1){
pthread_mutex_lock(&mutex);
if(a != b){
printf("a = %d,b = %d\n",a,b);
}
pthread_mutex_unlock(&mutex);
}
if(pthread_join(tid,NULL)){
perror("pthread_join");
exit(1);
}
return 0;
}
本文介绍了Linux环境下C语言的线程基础,包括线程的概念、资源分配,以及线程创建、结束等API函数的使用。同时,详细讲解了线程同步的信号量机制和线程互斥的概念,帮助理解多线程编程中的关键概念和技术。
609

被折叠的 条评论
为什么被折叠?



