第八章 多线程编程

(1) 线程的概念

  相信很多读者参加面试笔试的时候都被问及线程与进程的区别。我做概要分析如下

  1> 进程是线程的载体,一个进程包含一个以上的线程,这些线程运行在进程的地址空间。

  2>  进程是系统资源的分配拥有着,所有进程内的线程均可访问资源。

  3>  线程是处理器调度的基本单位,但进程不是  。

  4>  每一个进程都有一个主线程,其它线程均有主线程直接简介产生。

(2) 线程的控制  

#include <pthread.h> 
int pthread_create(pthread_t *restrict thread,  const pthread_attr_t *restrict attr,  void *(*start_routine)(void*), void *restrict arg);

void pthread_exit(void *value_ptr);   //终止线程本身
int pthread_join(pthread_t thread, void **value_ptr); //其它线程获取线程的退出码

pthread_join 获取相关线程推出的返回值。它的值和线程的返回方式有关

return        一般执行完毕      获取return 返回值一般是数字

pthread_exit     自己推出   返回退出制定指针的值

pthread_cancel  其它线程异常终止  返回PTHREAD_CANCELED

(3)远堆传参

  远堆传参,是指在堆中分配一块内存,又来作为线程启动的参数传递。如果是临时变量的话住线程创建玩子线程可能就会销毁临时变量。因为每个线程有各自的堆栈。所以子线程可能访问不到参数。这也是唯一不遵循内存分配原则的地方(谁分配谁释放)。主线程分配,子线程释放。 当然也可以使用全局变量。

(4)线程同步

  线程共享同一进程内的资源,为了防止访问冲突,所以需要同步。同步机制。

  1> 互斥锁   同一时刻只能一个线程访问该资源 即锁定 

#include <pthread.h> 
int pthread_mutex_destroy(pthread_mutex_t *mutex);   //销毁锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);//初始化 int pthread_mutex_lock(pthread_mutex_t *mutex); 
int pthread_mutex_trylock(pthread_mutex_t *mutex); 
int pthread_mutex_unlock(pthread_mutex_t *mutex);

   用pthread_mutex_init函数初始化的Mutex可以用pthread_mutex_destroy销毁。如果Mutex变量是静态分配的(全局变量或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相

当于用pthread_mutex_init初始化并且attr参数为NULL。pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  

  2> 信号量(Semaphore)

     Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1表示有一个可用资源,加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示

又有了一个可用资源。 信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1。不仅可用于同一进程的线程间同步,也可用于不同进程间的同步。  

#include <semaphore.h> 
 
int sem_init(sem_t *sem, int pshared, unsigned int value);   //初始化
int sem_wait(sem_t *sem);                      //等待  获取资源
int sem_trywait(sem_t *sem);                    //尝试等待   
int sem_post(sem_t * sem);                                    //释放 资源
int sem_destroy(sem_t * sem);                                 //销毁

  3> 条件变量  不太常用

#include <pthread.h> 
 
int pthread_cond_destroy(pthread_cond_t *cond); 
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); //pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_timedwait(pthread_cond_t *restrictcond, pthread_mutex_t *restrict mutex,  const struct timespec *restrict abstime); 

int pthread_cond_wait(pthread_cond_t *restrict cond,  pthread_mutex_t *restrict mutex); 

int pthread_cond_broadcast(pthread_cond_t *cond); 
int pthread_cond_signal(pthread_cond_t *cond); 

一个条件变量总是和一个互斥锁相关。所以使用起来比较繁琐,也不常用。下面是一个生产者和消费者的例子供学习参考。

#include <stdlib.h> 
#include <pthread.h> 
#include <stdio.h> 
 
struct msg
{ 
   struct msg *next; 
    int num; 
}; 
 
struct msg *head; 
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 
 
void *consumer(void *p)
{
  struct msg *mp; 
 
  for (;;) 
 { 
     pthread_mutex_lock(&lock); 
     while (head == NULL) 
     pthread_cond_wait(&has_product, &lock);   
     mp = head; 
     head = mp->next; 
     pthread_mutex_unlock(&lock); 
     printf("Consume %d
", mp->num); 
     free(mp); 
     sleep(rand() % 5); 
 } 
} 
void *producer(void *p) 
{ 
     struct msg *mp; 
     for (;;) 
    { 
         mp = malloc(sizeof(struct msg)); 
         mp->num = rand() % 1000 + 1; 
         printf("Produce %d
", mp->num); 
         pthread_mutex_lock(&lock); 
         mp->next = head; 
         head = mp; 
         pthread_mutex_unlock(&lock); 
         pthread_cond_signal(&has_product); 
         sleep(rand() % 5); 
     } 
} 
 
int main(int argc, char *argv[])  
{ 
     pthread_t pid, cid;   
 
      srand(time(NULL)); 
      pthread_create(&pid, NULL, producer, NULL); 
      pthread_create(&cid, NULL, consumer, NULL); 
      pthread_join(pid, NULL); 
      pthread_join(cid, NULL); 
      return 0; 
} 

附加:pthread_detach

  创建一个线程默认的状态是joinable, 如果一个线程运行结束但没有被join,则它的状态类似于进程中的Zombie Process(僵尸进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用 pthread_join 来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)

  但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此

  这时可以在子线程中加入代码    

    pthread_detach(pthread_self ())

  或者父线程调用
    pthread_detach(thread_id)(非阻塞,可立即返回)
  这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
 
原文地址:https://www.cnblogs.com/wolfrickwang/p/3186442.html