线程

1. 创建线程
创建线程使用pthread_create函数,函数原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
引用该函数需要包含头文件pthread.h,在编译时还需要加上-lpthread选项用来链接线程库。第一个参数为pthread_t类型指针,在线程创建时,用来获取线程ID,用来标志新创建的线程,第二个参数为线程属性,一般不需要特殊的属性,所以可以设置该参数为NULL,第三个参数为新创建线程的函数入口地址,新创建的线程就从该函数开始执行,最后一个参数为传递给该函数的参数。创建成功返回0,如果失败则返回错误码。

2. 线程退出
线程退出使用pthread_exit函数,函数原型如下:
void pthread_exit(void *retval);
就如同进程使用exit函数一样,线程退出使用pthread_exit函数,也可以使用return直接返回。但绝不能使用exit来退出一个线程,这样将导致整个程序的退出,而不仅仅是该线程。其中retval为指向某个对象的指针 。

3. 线程等待
等待一个进程的退出是使用wait函数,等待一个线程退出则使用pthread_join函数,函数原型如下:
int pthread_join(pthread_t thread, void **retval);
第一个参数等待线程的ID,第二个参数为一个指针,它指向另一个指针,这个指针就是线程的返回值。与pthread_create类似,该函数在成功时返回0,失败时返回错误码。

4. 一个完整的例子
#include <stdio.h>
#include <pthread.h>

void *my_thread(void *arg)
{
        printf("BBB\n");

        pthread_exit(NULL);
}

int main(void)
{
        pthread_t thread_id;

        if (pthread_create(&thread_id, NULL, my_thread, NULL)) {
                printf("Can't create a new thread!\n");
                return -1;
        }

        if (pthread_join(thread_id, NULL)) {
                printf("Wait a thread exit error!\n");
                return -1;
        }

        printf("AAA\n");

        return 0;
}
以上程序为一个完整的线程操作实例,在线程创建好之后,先等待新创建线程的退出,这样会先打印出"BBB"信息,最后新创建的线程退出之后,才打印"AAA"信息,最后程序退出。


5. 两个线程同时执行
程序实例如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *my_thread(void *arg)
{
        int i = 10;

        while (i--) {
                printf("BBB\n");
                sleep(1);
        }

        pthread_exit(NULL);
}

int main(void)
{
        pthread_t thread_id;
        int i = 10;

        if (pthread_create(&thread_id, NULL, my_thread, NULL)) {
                printf("Can't create a new thread!\n");
                return -1;
        }

        while (i--) {
                printf("AAA\n");
                sleep(1);
        }

        if (pthread_join(thread_id, NULL)) {
                printf("Wait a thread exit error!\n");
                return -1;
        }

        return 0;
}
具体先打印"AAA"还是先打印"BBB",这就要看具体的进程调度算法,但肯定是交替打印,因为在打印一条信息之后调用了sleep函数,该函数将使线程进入睡眠,sleep函数原型如下:
unsigned int sleep(unsigned int seconds);
引用该函数需要包含头文件unistd.h,参数seconds为睡眠的秒数。

6. 线程同步问题
(1) 信号量
信号量使用sem_init函数做初始化,函数原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
引用该函数需要包含头文件semaphore.h,第一个参数为初始化的信号量对象,第二个参数为是否在多个进程中共享,0表示不共享,只在当前进程中使用,非0值表示在多个进程中共享该信号量,最后>一个参数为信号量的一个初始值。sem_init函数调用成功返回0,否则返回-1。

sem_post函数的作用是以原子操作的方式给信号量的值加1,函数原型如下:
int sem_post(sem_t *sem);
sem为信号量对象,函数调用成功返回0,否则返回-1。

sem_wait函数的作用是以原子操作的方式给信号量的值减1,函数原型如下:
int sem_wait(sem_t *sem);

sem_destroy函数同sem_init的作用刚好相反,它是对已经使用完的信号量做后续处理,函数原型如下:
int sem_destroy(sem_t *sem);

信号量实例如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

#define N 100

sem_t mutex;
sem_t empty;
sem_t full;

char buf[N];

int count = 0;

void *producer(void *arg)
{
        while (1) {
                sem_wait(&mutex);
                sem_wait(&empty);
                buf[count++] = 'a';
                printf("count = %d\n", count);
                sem_post(&full);
                sem_post(&mutex);
                sleep(1);
        }
}

void *consumer(void *arg)
{
        while (1) {
                sem_wait(&mutex);
                sem_wait(&full);
                buf[--count] = '\0';
                printf("count = %d\n", count);
                sem_post(&empty);
                sem_post(&mutex);
                sleep(2);
        }
}

int main(void)
{
        pthread_t producer_id;
        pthread_t consumer_id;
        int ret = 0;

        if (sem_init(&mutex, 0, 1)) {
                printf("mutex semaphore initialization error!\n");
                ret = -1;
                goto err;
        }

        if (sem_init(&empty, 0, N)) {
                printf("empty semphore initialization error!\n");
                ret =  -1;
                goto err1;
        }

        if (sem_init(&full, 0, 0)) {
                printf("full semphore initialization error!\n");
                ret = -1;
                goto err2;
        }

        if (pthread_create(&producer_id, NULL, producer, NULL)) {
                printf("Can't create producer thread!\n");
                ret = -1;
                goto err3;
        }

        if (pthread_create(&consumer_id, NULL, consumer, NULL)) {
                printf("Can't create consumer thread!\n");
                ret = -1;
                goto err3;
        }

        if (pthread_join(producer_id, NULL)) {
                printf("Wait producer thread exit error!\n");
                ret = -1;
                goto err3;
        }

        if (pthread_join(consumer_id, NULL)) {
                printf("Wait consumer thread exit error!\n");
                ret = -1;
                goto err3;
        }

err3:
        sem_destroy(&full);
err2:
        sem_destroy(&empty);
err1:
        sem_destroy(&mutex);
err:
        return ret;
}

以上是一个生产者和消费的例子,在缓冲区已满的情况下,生产者不能往缓冲区写入数据,在缓冲区为空的情况下,消费者不能从缓冲区读走数据。这里用到了三个信号量,其中mutex信号量只有两种取值0和1,也称为二元信号量,用来确保生产者和消费者不会同时访问缓冲区,后面使用互斥锁也能实现同样的功能。empty信号量用来记录缓冲区剩余的空间大小,初值为100,full信号量用来记录缓冲区已有数据空间大小,初值为0,即一开始缓冲区没有数据,为空。信号量多用于数据的同步,例如这里的生产者与消费者,没有数据的时候,消费者是不能访问缓冲区的,而在缓冲区已满的情况下,生产者是不能访问缓冲区的。
还有一点需要注意的是,sem_wait会阻塞当前线程,直到信号量值不为0为止,如果需要非阻塞就使用sem_trywait函数。


(2) 互斥锁
互斥锁使用pthread_mutex_init做初始化,函数原型如下:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

加锁使用pthread_mutex_lock函数,函数原型如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);

解锁使用pthread_mutex_unlock,函数原型如下:
int pthread_mutex_unlock(pthread_mutex_t *mutex);

同sem_destroy函数一样,不再使用互斥锁时候需要使用pthread_mutex_destroy函数做后续的处理工作,函数原型如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex);

同Linux下大多函数的返回值一样,成功时返回0,失败时返回错误码。
前面就说过,可以使用互斥锁对缓冲区加保护,关键代码如下:
pthread_mutex_t mutex_lock;

void *producer(void *arg)
{
        while (1) {
                pthread_mutex_lock(&mutex_lock);
                sem_wait(&empty);
                buf[count++] = 'a';
                printf("count = %d\n", count);
                sem_post(&full);
                pthread_mutex_unlock(&mutex_lock);
                sleep(1);
        }
}

void *consumer(void *arg)
{
        while (1) {
                pthread_mutex_lock(&mutex_lock);
                sem_wait(&full);
                buf[--count] = '\0';
                printf("count = %d\n", count);
                sem_post(&empty);
                pthread_mutex_unlock(&mutex_lock);
                sleep(2);
        }
}

int main(void)
{
        pthread_mutex_init(&mutex_lock, NULL);
        ...
}
互斥锁只有两种取值0和1,pthread_mutex_lock为阻塞版本,同信号量一样互斥锁也有非阻塞版本,函数为 pthread_mutex_trylock。

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