Unix编程-线程

Posted PorFavor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unix编程-线程相关的知识,希望对你有一定的参考价值。

线程

进程是一个程序的一个实例,拥有自己独立的各种段(数据段,代码段等等),每次创建一个进程需要从操作系统分配这些资源给他,消耗一定的时间,在linux下C语言创建一个进程使用fork()函数;

线程是一个轻量级的进程,除了自己少数的资源,不用用其他资源,且一个进程可以创建多个线程,这些线程共享进程的资源,创建线程的时间要比创建进程少很多,从函数角度是使用clone()创建。

使用线程处理文件I/O或者socket处理都是非常有优势的,将一个大任务分解成若干个小任务,每个线程处理一个任务,线程之间切换不需要花很多时间,而且线程之间数据交换很方便,共享存储区。


创建线程

int pthread_create(pthread_t * tid, const pthread_attr_t * attr, void * ( * func) (void * ), void * arg);

其返回值是一个整数,若创建进程成功返回0,否则,返回其他错误代码。

pthread_t * tid 线程变量名:pthread_t *类型,是标示线程的id,一般是无符号整形,这里也可以是引用类型,目的是用于返回创建线程的ID

const pthread_attr_t * attr 线程的属性指针:制定线程的属性,比如线程优先*级,初始栈大小等,通常情况使用的都是指针。默认为NULL。

void * ( * func) (void * ) 创建线程的程序代码:一般是函数指针,进程创建后执行该函数指针指向的函数。

void * arg 程序代码的参数:若线程执行的函数包含由若干个参数,需要将这些参数封装成结构体,并传递给它指针。


pthread_t pthread_self (void);

用于返回当前进程的ID


int pthread_equal(pthread_t thread1, pthread_t thread2)

判断两个线程描述符是否指向同一线程。在LinuxThreads中,线程ID相同的线程必然是同一个线程


结束线程

void pthread_exit (void *status);

status: 指针类型,用于存储线程结束后返回状态。


线程等待

int pthread_join (pthread_t tid, void ** thread_return);

pthread_t tid:要等待结束的线程的标识

void **thread_return 指针thread_return指向的位置存放的是终止线程的返回状态。默认为NULL。

pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度。

如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。

举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。


分离线程

int pthread_detach (pthread_t tid);

pthread_t tid: 指定线程的ID

指定的ID的线程变成分离状态;若指定线程是分离状态,则 如果线程退出,那么它所有的资源都将释放。

对于一个处于分离状态的线程,不可使用pthread_join。

如果线程不是分离状态,线程必须保留它的线程ID、退出状态,直到其他线程对他调用pthread_join()函数


互斥锁

多线程之间可能需要互斥的访问一些全局变量,这就需要互斥地来访问,这些需要共享访问的字段被称作是临界资源,访问临界资源的程序段称作是临界区。

实现线程间的互斥与同步机制的是锁机制。

pthread_mutex_t mutex 锁对象

pthread_mutex_init(&mutex,NULL) 在主线程中初始化锁为解锁状态

pthread_mutex_lock(&mutex): 访问临界区加锁操作

pthread_mutex_unlock(&mutex): 访问临界区解锁操作


例 对全局变量的互斥锁

使用三个线程,求从 1 加到 10000 的值

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>


int sharei = 0;

void increase_num(void);

// add mutex

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


int main()

{

    int ret;

    pthread_t thread1,thread2,thread3;

    ret = pthread_create(&thread1,NULL,(void *)&increase_num,NULL);

    ret = pthread_create(&thread2,NULL,(void *)&increase_num,NULL);

    ret = pthread_create(&thread3,NULL,(void *)&increase_num,NULL);


    pthread_join(thread1,NULL);

    pthread_join(thread2,NULL);

    pthread_join(thread3,NULL);


    printf("sharei = %d\n",sharei);


    return 0;

}


void increase_num(void)

{

    long i,tmp;

    for(i =0;i<=10000;++i){

        // lock

        if(pthread_mutex_lock(&mutex) != 0){

            perror("pthread_mutex_lock");

            exit(EXIT_FAILURE);

        }

        tmp = sharei;

        tmp = tmp + 1;

        sharei = tmp;

        // unlock

        if(pthread_mutex_unlock(&mutex) != 0){

            perror("pthread_mutex_unlock");

            exit(EXIT_FAILURE);

        }

    }

}


其实这里的加锁不是对共享变量(全局变量)或者共享内存进行保护,这里的加锁实际上是对临界区的控制,所谓的临界区就是访问临界资源的那一段代码,

这段代码对临界资源进行多种操作,正确的情况是不允许这段代码执行到一半,处理器使用权就被其他线程抢走,

所以这段代码具有原子性,即要么执行,要么不执行,不能执行到一半就被抢走处理权,这样就会造成共享数据被污染。

还有一点,添加锁来控制临界区是有代价的,这个代价表现出来就是时间的额外开销,内部过程是因为要保护现场,会利用一些资源,也需要处理器处理的时间。


条件变量

线程a等待某个条件成立,条件成立,线程a才继续向下执行。线程b的执行使条件成立,条件成立之后唤醒线程a,以继续执行。这个条件就是条件变量,pthread_cond_t 就是条件变量类型。

https://blog.csdn.net/weixin_38239856/article/details/78255767





以上是关于Unix编程-线程的主要内容,如果未能解决你的问题,请参考以下文章

UNIX环境高级编程笔记之线程

Unix编程-线程

Unix系统编程_cha11.6_线程同步

unix高级编程之线程

Unix/Linux 编程:网络编程之 线程池

Unix环境高级编程线程