线程-线程函数接口

  • Post author:
  • Post category:其他



目录


一,线程


1.概念


2.进程和线程的区别


3.线程资源


二,线程函数


1.线程创建


2.线程退出与线程回收


3.线程同步(信号量)


4.线程互斥与线程条件变量


1.线程互斥


2.线程条件变量


一,线程

1.概念

线程相当于一个轻量级的进程,可以提高系统的性能;在Linux系统中,同样使用结构体task_struct来描述一个线程;线程和进程都参加统一的调度。

在同一个进程中创建的线程共享该进程的地址空间。

2.进程和线程的区别

共同点:都为操作系统提供了


并发执行


的能力;

不同点:(1)线程是系统


调度


的最小单位;进程是


资源


分配的最小单位;

(2)同一个进程创建的多个线程


共享


进程的资源;进程的地址空间相互


独立


(3)线程通信相对简单,可通过


全局变量


实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制


(3G-4G的内核空间)


(4)线程安全性较差,当进程结束时会导致所有的线程退出,一个线程的结束可能会影响到其他线程;进程相对安全。

3.线程资源

共享的资源:可执行的指令,静态数据,进程中打开的文件描述符,信号处理函数,当前工作目录,用户ID,用户组ID。

私有的资源:线程ID(TID),PC(程序计数器)和相关寄存器,堆栈,错误码,信号掩码,优先级,执行状态和属性。

二,线程函数



编译包含线程的程序时,需要链接库“-lpthread”。

1.线程创建


函数:int

pthread_create

(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);

功能:创建线程;

参数:thread->线程标识;

attr->线程属性,常设置为默认属性NULL;

start_toutine->函数名,代表线程函数,函数的参数应为void *arg;

arg->用来给线程函数传参;

返回值:成功->0;失败->错误码;

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//线程函数
void *handler(void *arg)
{
    printf("in thread\n");
}
int main(int argc, char const *argv[])
{
    //定义pthread_t类型的tid
    pthread_t tid;
    //判断,返回值不为0则创建失败
    if(pthread_create(&tid,NULL,handler,NULL) != 0)
    {
        perror("thread create err");
        return -1;
    }
    printf("in main\n");
    //不一定会进入线程,所以使用循环保证线程运行
    while(1);
    return 0;
}

2.线程退出与线程回收


(1) 函数:int

pthread_exit

(void *value_ptr);

功能:执行线程退出的功能;

参数:value_ptr->线程退出时返回的值,一般设置为NULL;

返回值:成功->0;失败->错误码;


(2) 函数:int

pthread_join

(pthread_t thread,void **value_ptr);

功能:用于等待一个指定的线程结束;

参数:thread->创建的线程对象;

value_ptr->指针*value_ptr指向线程返回的参数,一般设置为NULL;

返回值:成功->0;失败->错误码。

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *handler(void *arg)
{
    printf("in thread\n");
	//pthread_exit(NULL);//结束线程
	pthread_exit("hello");
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    if(pthread_create(&tid,NULL,handler,NULL) != 0)
    {
        perror("thread create err");
        return -1;
    }
	
    printf("in main\n");
    //接收线程返回的参数hello
	void *ch;
	pthread_join(tid,&ch);
	printf("%s\n",(char *)ch);

	//pthread_join(th,NULL);//回收线程资源
    return 0;
}

3.线程同步(信号量)

(1)概念:


同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情;

(2)信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待;

信号量代表某一类资源,其值表示系统中该资源的数量;

信号量是一个受保护的变量,只能通过三种操作来访问:初始化、P操作(申请资源)、V操作(释放资源);

信号量的值为非负整数。

(3)函数接口


①int

sem_init

(sem_t *sem,  int pshared,  unsigned int value)

功能:初始化信号量;

参数:sem->初始化的信号量对象;

pshared->信号量共享的范围

(0: 线程间使用   非0:1进程间使用);

value->信号量初值;

返回值:成功为0,失败为-1;

sem_t sem;
if(sem_init(&sem,0,0) < 0)
	{
		perror("sem init err");
		return -1;
	}


②int

sem_wait

(sem_t *sem)

功能:申请资源,

P

操作;

参数:sem->信号量对象;

返回值:成功为0,失败为-1;


注:此函数执行过程,执行到wait,先对信号量进行减1,若减完的信号量的值大于等于0,表示有资源可以用,则继续执行;当信号量的值小于0时,表示没有资源可以使用,函数阻塞。


③int

sem_post

(sem_t *sem)

功能:释放资源,

V

操作;

参数:sem->信号量对象;

返回值:成功为0,失败为-1;

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>

char ch[32] = "";
sem_t sem;
void *handler(void *arg)
{
	while(1)
	{
		//给信号量 -1
		sem_wait(&sem);

		if((strcmp(ch,"quit") == 0))
		{
			pthread_exit(NULL);
		}
		printf("thread\n");
		printf("output:%s\n",(char *)arg);
	
	}
}

int main(int argc, char const *argv[])
{
	//创建线程
    pthread_t tid;
	//给线程函数传递参数ch,先强转为void *类型
   	if(pthread_create(&tid,NULL,handler,(void *)&ch) != 0)
    {
        perror("thread create err");
       	return -1;
    }
	//初始化信号量(信号量对象,0->线程间使用,信号量初值)
	if(sem_init(&sem,0,0) < 0)
	{
		perror("sem init err");
		return -1;
	}
	while(1)
	{
		scanf("%s",ch);
		//信号量+1
		sem_post(&sem);
		if(strcmp(ch,"quit")==0)
		break;
	}
	pthread_join(tid,NULL);
	printf("main\n");
    return 0;
}

执行结果:输入一串字符串后,信号量先增加1,如果字符串不是“quit”,则线程中的wait判断信号量不为0,字符串也不为“quit”,就输出字符串,然后如果线程继续循环,wait检测到信号量为0时,将会继续运行主进程的循环,继续输入字符串,信号量再+1。

4.线程互斥与线程条件变量

1.线程互斥

1.相关概念

(1)临界资源:一次仅允许一个进程所使用的资源;

(2)临界区:指的是一个访问共享资源的程序片段;

(3)互斥:多个线程在访问临界资源时,同一时间只能被一个线程访问;

(4)互斥锁:通过互斥锁可以实现互斥机制,使用互斥锁的目的是用来保护临界资源,每个临界资源都应该由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。

2.函数接口


(1)int

pthread_mutex_init

(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)

功能:初始化互斥锁;

参数:mutex ->互斥锁,用pthread_mutex_t来定义该参数;

pthread_mutex_t lock;

attr ->互斥锁属性 ,一般设置为NULL;

返回值:成功为0,失败为-1;


(2)int

pthread_mutex_lock

(pthread_mutex_t *mutex)

功能:

申请

互斥锁;

参数:mutex->互斥锁;

//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
    {
        perror("mutex init err");
        return -1;
    }
//上锁
pthread_mutex_lock(&lock);

返回值:成功为0,失败为-1;

注:pthread_mutex_lock和pthread_mutex_trylock区别:pthread_mutex_lock申请不到锁是会阻塞函数的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回。


(3)int

pthread_mutex_unlock

(pthread_mutex_t *mutex)

功能:

释放

互斥锁;

参数:mutex->互斥锁;

//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
    {
        perror("mutex init err");
        return -1;
    }
//上锁
pthread_mutex_lock(&lock);
//解锁
pthread_mutex_unlock(&lock);

返回值:成功为0,失败为-1。


(4)int

pthread_mutex_destroy

(pthread_mutex_t *mutex)

功能:销毁互斥锁;

参数:mutex->互斥锁;

代码演示:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

//互斥锁参数
pthread_mutex_t lock;

int a[10] = {0,1,2,3,4,5,6,7,8,9};
int i;
//线程函数
void *handler_swap(void *arg)
{
    int temp;
    while(1)
    {
        //上锁
        pthread_mutex_lock(&lock);
        for(i = 0;i < 5;i++)
        {
            temp = a[i];
            a[i] = a[9-i];
            a[9-i] = temp;
        }
        //解锁
        pthread_mutex_unlock(&lock);
    }
}

//线程函数
void *handler_printf(void *arg)
{
    while(1)
    {
        //上锁
        pthread_mutex_lock(&lock);
        for(i = 0;i < 9;i++)
        {
            printf("%d ",a[i]);
        }
        putchar(10);
        //解锁
        pthread_mutex_unlock(&lock);
        sleep(1);
    }
}
int main(int argc, const char *argv[])
{
    //创建两个线程
    pthread_t tid1,tid2;
    if(pthread_create(&tid1,NULL,handler_swap,NULL)!=0)
    {
        perror("thread create err");
        return -1;
    }
    if(pthread_create(&tid2,NULL,handler_printf,NULL)!=0)
    {
        perror("thread create err");
        return -1;
    }
    
    //初始化锁
    if(pthread_mutex_init(&lock,NULL)<0)
    {
        perror("mutex init err");
        return -1;
    }

    //回收线程资源
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    return 0;
}

执行结果为:在终端中循环正序输出0到9,或9到0,不能确定输出的次数,但是不会打乱一次输出中的顺序。

2.线程条件变量



条件变量


需要与


互斥锁


搭配使用,实现


同步机制




(1)int




pthread_cond_init




(pthread_cond_t *restrict cond,const pthread_condattr_t


*restrict attr);


功能:



初始化



条件变量;

参数:cond->是一个指向结构

pthread_cond_t

的指针;
pthread_cond_t cond;
pthread_cond_init(&cond,NULL);

restrict attr->是一个指向结构pthread_condattr_t的指针,

一般设为



NULL;


返回值:成功为0,


失败为非0。

初始化时,通过判断返回值是否小于0,来确定是否初始化成功,小于0则初始化失败



(2)int




pthread_cond_wait




(pthread_cond_t


*restrict cond,pthread_mutex_t


*restrict mutex);


功能:



等待信号



的产生;

参数:restrict cond->要等待的条件;

restrict mutex->对应的锁;

返回值:成功为0


,失败不为0。

注:当没有条件产生时->函数阻塞,锁解开;

等待到条件产生->函数结束阻塞,同时上锁。
pthread_cond_wait(&cond,&lock);



(3)int




pthread_cond_signal




(pthread_cond_t


*cond);


功能:给条件变量发送信号,



产生信号;


参数:cond->条件变量值;
pthread_cond_signal(&cond);

返回值:成功为0,失败为非0;


注:必须等待pthread_cond_wait函数先执行,再产生条件信号。


此函数和pthread_cond_broadcast区别是:


pthread_cond_broadcast函数相当于是广播,会将所有等待此条件的线程唤;


pthread_cond_signal只能唤醒单个等待此条件的线程。



(4)int




pthread_cond_destroy




(pthread_cond_t


*cond);


功能:将条件变量销毁;

参数:cond->条件变量值;

返回值:成功为


0


,失败为非0;
代码演示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//函数pthread_mutex_init()中的参数
pthread_mutex_t lock;
//函数pthread_cond_init()中的参数
pthread_cond_t cond;
int money = 0;

void *handler_get(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&lock);//上锁
        if(money < 100)
        {
            //当没有条件cond产生时阻塞,同时将锁打开;
            //等待到条件产生时,函数立即返回同时进行上锁;
            pthread_cond_wait(&cond,&lock);
        }
        if(money >= 100)
        {
            money -= 100;
            printf("money:%d\n",money);
        }
        pthread_mutex_unlock(&lock);//解锁
    }
}

void *handler_save(void *arg)
{
    while(1)
    {
        sleep(1);//睡眠一秒确保get线程先执行
        pthread_mutex_lock(&lock);//上锁
        scanf("%d",&money);
        pthread_mutex_unlock(&lock);//解锁

        pthread_cond_signal(&cond);//产生cond条件(信号);
    }
}
int main(int argc, char const *argv[])
{
    pthread_t tid1,tid2;
    //创建线程:tid->pthread_t,NULL,线程函数名,函数参数
    if(pthread_create(&tid1,NULL,handler_get,NULL)!=0)
    {
        perror("create tid1 err");
        return -1;
    }
    if(pthread_create(&tid2,NULL,handler_save,NULL)!=0)
    {
        perror("create tid2 err");
        return -1;
    }
    
    //初始化锁
    //参数:取地址-定义的pthread_mutex_t lock,状态NULL
    //返回值小于0时,创建失败
    if(pthread_mutex_init(&lock,NULL) < 0)
    {
        perror("mutex init err");
        return -1;
    }

    //初始化条件变量
    //参数:取地址-定义的pthread_cond_t cond,状态NULL
    //返回值小于0时,创建失败
    if(pthread_cond_init(&cond,NULL) < 0)
    {
        perror("cond init err");
        return -1;
    }

    //回收线程资源
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    return 0;
}

执行结果:

如果本文中存在概念错误或代码错误的情况,请批评指正。



版权声明:本文为m0_68210771原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。