linux多线程编程基础函数介绍

linux多线程编程基础函数介绍

Linux操作系统提供了pthread线程库,它是符合POSIX标准的函数库。线程控制方面的函数定义在pthread.h文件中,信号量控制方面的函数定义在semaphore.h文件中。

  • 线程控制方面的函数有:pthread_attr_init、pthread_create、pthread_join、pthread_exit
  • 互斥锁机制函数:pthread_mutex_init、 pthread_mutex_lock、 pthread_mutex_unlock、 pthread_mutex_destroy
  • 条件变量函数:pthread_cond_init、int pthread_cond_signal、int pthread_cond_wait、int pthread_cond_destroy、pthread_cond_broadcast
  • 信号量线程控制函数:sem_init、sem_wait、sem_post、sem_getvalue、sem_destory

本文中包含以上大部分函数的基本用法,用于后期一道大程的设计和编写。其中信号量这一块不单独属于多线程编程部分,在此暂时先不讲。

参考书籍:unix高级编程第11章。


线程的标识

线程的ID: 用pthread_t的数据类型表示
ID比较:int pthread_equal(pthread_t tid1, pthread_t tid2)
ID获取:pthread_t pthread_self(void)


线程的创建、等待、结束

线程的创建:
int pthread_create(pthread_t restricttidp, const pthread_attr_t *restrict attr,void (start_rtn)(void), void *restrict arg);
其中,tidp指向的内存单元存储新创建线程的线程ID,attr先设置为null, 新创建的进程从start_rtn的函数地址开始运行,arg为需要传递的参数。

线程的等待:int pthread_join(pthread_t thread, void **rval_ptr);
线程的结束:int pthread_exit(void *rval_ptr);
线程的取消:int pthread_cancel(pthread_t tid);
一般线程的结束可以通过三种途径:线程执行完毕,线程被同一进程中的其他线程取消(pthread_cancel),自己调用pthread_exit。注意,pthread_cancel不等待线程终止,仅向线程提出取消请求。
再来解释一下rval_ptr参数。当线程1采用err = pthread_join(thread2, &res)等待线程2的时候,线程2调用pthread_exit((void *)1),此时res值即为exit返回的参数1。同时还可以通过这个指针来传递复杂的结构体,但必须注意在传递回线程1后内存仍必须是有效的。

下面的程序用多线程实现斐波那契数列的计算。其中子线程用于计算斐波那契数列中每一个项的值,主线程负责输出。

#include 
#include 
#include 
#include 

pthread_t thread_id;
int num;

void *thread_calc(int arr[])
{
    for (int i=2; i<=num; i++)
        arr[i] = arr[i-1] + arr[i-2];
    return NULL;
    pthread_exit(NULL);
}

int main()
{
    int* arr;

    scanf("%d", &num);
    arr = (int*)malloc(sizeof(int) *(num+1));
    arr[0] = 0;
    arr[1] = 1;

    int res = pthread_create(&thread_id, NULL, thread_calc, arr);
    if (res == -1) printf("Thread create failed!");
    printf("Waiting for the thread to compute!\n");
    pthread_join(thread_id, NULL);

    for (int i=0; i<=num; i++)
        printf("array[%d] = %d\n",i,arr[i]);
}

线程同步

线程的同步主要通过互斥变量来完成。简单的来说就是跟数据库里一样,对这个程序正在进行读写的资源加锁,其他任意想要访问这个资源的线程都会被阻塞。互斥变量用pthread_mutex_t数据类型来表示,初始化方式有两种:

  • 把其设置为常量PTHREAD_MUTE_INITIALIZER(只适用于静态分配的互斥量)
  • 调用int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

同时如果动态分配互斥量(malloc),需要用pthread_mutex_destory(pthread_mutex_t *mutex)来释放内存。
对互斥量加锁解锁的函数为以下三个:

  • int pthread_mutex_lock(pthread_mutex_t *mutex);
  • int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • int pthread_mutex_trylock(pthread_mutex_t *mutex);
    以下代码为一个很简答的实例,两个子线程分别对global_count进行加减,同时调用sleep使得每两次调用sub才会调用一次add。
#include 
#include 
#include   
//unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。
#include 

void* thread_add();
void* thread_sub();

pthread_t th_add, th_sub; 
pthread_mutex_t lock;
int global_count = 0;

int main()
{
    int res;

    res = pthread_mutex_init(&lock, NULL);
    if (res == -1) {
        printf("Create mutex failed!\n");
        exit(1);
    }

    res = pthread_create(&th_add, NULL, thread_add, NULL);
    if (res == -1) {
        printf("Create thread failed!\n");
        exit(1);
    }

    res = pthread_create(&th_sub, NULL, thread_sub, NULL);
    if (res == -1) {
        printf("Create thread failed!\n");
        exit(1);
    }
    pthread_join(th_add, NULL);
    pthread_join(th_sub, NULL);
}

void* thread_add()
{
    while (global_count < 20) {

        sleep(2);
        pthread_mutex_lock(&lock);
        printf("Before add: %d\n", global_count);
        global_count += 5;
        printf("After add: %d\n", global_count);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(NULL);
}

void* thread_sub()
{
    while (global_count > -20) {

        sleep(1);
        pthread_mutex_lock(&lock);
        printf("Before sub: %d\n", global_count);
        global_count -= 5;
        printf("After sub: %d\n", global_count);
        pthread_mutex_unlock(&lock);
    }
    pthread_exit(NULL);
}

如果一段程序有种两个互斥量(锁)的时候,就很容易出现死锁的情况。死锁为线程1锁住资源a,同时正在请求资源b,同时线程2锁住资源b,同时正在请求资源a。该情况复杂的多,具体实例可以详见unix高级编程第11章6.2。


条件变量

条件变量的数据类型:
pthread_cond__t

条件变量的创建:
intpthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr,);

条件变量的反初始化:
intpthread_cond_destroy(pthread_cond_t *cond);

主要的三个函数:
int pthread_cond_wait(pthread_cond_t *restrict bond, pthread_mutex_t *restrict mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

其中,在调用wait的时候,需要将互斥量一起传给函数。函数在收到互斥量并开始等待后,会先将互斥量解锁,方便其他线程先运行。signal和broadcast用于通知wait,其中signal通知一个及以上,broadcast通知所有的等待在消息队列中的条件。

你可能感兴趣的:(linux多线程)