linux下的多线程编程备忘



参考资料:

Linux 的多线程编程的高效开发经验

linux多线程编程,你还在用sleep么?用pthread_cond_timedwait吧

Linux多线程编程(不限Linux)


Linux下多线程开发API

a.对线程进行操作的API

1 #include <pthread.h>
2 int pthread_create( pthread_t * tid , const pthread_attr_t * attr , void *( * func) ( void * ), void * arg);
3 int pthread_join ( pthread_t tid , void ** status);
4 pthread_t pthread_self ( void);
5 int pthread_detach ( pthread_t tid);
6 void pthread_exit ( void * status);

pthread_create用于创建一个线程,成功返回0,否则返回Exxx(为正数)。

  • pthread_t *tid:线程id的类型为pthread_t,通常为无符号整型,当调用pthread_create成功时,通过*tid指针返回。
  • const pthread_attr_t *attr:指定创建线程的属性,如线程优先级、初始栈大小、是否为守护进程等。可以使用NULL来使用默认值,通常情况下我们都是使用默认值。
  • void *(*func) (void *):函数指针func,指定当新的线程创建之后,将执行的函数。
  • void *arg:线程将执行的函数的参数。如果想传递多个参数,请将它们封装在一个结构体中。

pthread_join用于等待某个线程退出,成功返回0,否则返回Exxx(为正数)。

  • pthread_t tid:指定要等待的线程ID
  • void ** status:如果不为NULL,那么线程的返回值存储在status指向的空间中(这就是为什么status是二级指针的原因!这种才参数也称为“值-结果”参数)。

pthread_self用于返回当前线程的ID。

pthread_detach用于是指定线程变为分离状态,就像进程脱离终端而变为后台进程类似。成功返回0,否则返回Exxx(为正数)。变为分离状态的线程,如果线程退出,它的所有资源将全部释放。而如果不是分离状态,线程必须保留它的线程ID,退出状态直到其它线程对它调用了pthread_join

pthread_exit用于终止线程,可以指定返回值,以便其他线程通过pthread_join函数获取该线程的返回值。

  • void *status:指针线程终止的返回值。
b.对互斥锁的操作
#include <pthread.h> 

pthread_cond_wait用于等待某个特定的条件为真,pthread_cond_signal用于通知阻塞的线程某个特定的条件为真了。在调用者两个函数之前需要声明一个pthread_cond_t类型的变量,用于这两个函数的参数。

为什么条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的呢?因为“某个特性条件”通常是在多个线程之间共享的某个变量。互斥锁允许这个变量可以在不同的线程中设置和检测。

通常,pthread_cond_wait只是唤醒等待某个条件变量的一个线程。如果需要唤醒所有等待某个条件变量的线程,需要调用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默认情况下面,阻塞的线程会一直等待,知道某个条件变量为真。如果想设置最大的阻塞时间可以调用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果时间到了,条件变量还没有为真,仍然返回,返回值为ETIME。

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t * mptr); 
int pthread_mutex_unlock(pthread_mutex_t * mptr); 
在对临界资源进行操作之前需要 pthread_mutex_lock 先加锁,操作完之后 pthread_mutex_unlock 再解锁。而且在这之前需要声明一个 pthread_mutex_t 类型的变量,用作前面两个函数的参数
c.对条件变量的操作
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); 
int pthread_cond_signal(pthread_cond_t *cptr); 
int pthread_cond_broadcast (pthread_cond_t * cptr);
int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

pthread_cond_wait用于等待某个特定的条件为真,pthread_cond_signal用于通知阻塞的线程某个特定的条件为真了。在调用者两个函数之前需要声明一个pthread_cond_t类型的变量,用于这两个函数的参数。

为什么条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的呢?因为“某个特性条件”通常是在多个线程之间共享的某个变量。互斥锁允许这个变量可以在不同的线程中设置和检测。

通常,pthread_cond_wait只是唤醒等待某个条件变量的一个线程。如果需要唤醒所有等待某个条件变量的线程,需要调用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默认情况下面,阻塞的线程会一直等待,知道某个条件变量为真。如果想设置最大的阻塞时间可以调用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果时间到了,条件变量还没有为真,仍然返回,返回值为ETIME。


使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量始终与互斥锁一起使用。对条件的测试是在互斥锁(互斥)的保护下进行的。

如果条件为假,线程通常会基于条件变量阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:

  • 唤醒
  • 再次获取互斥锁
  • 重新评估条件

在以下情况下,条件变量可用于在进程之间同步线程:

  • 线程是在可以写入的内存中分配的
  • 内存由协作进程共享

当pthread_cond_timedwait()被调用时,调用线程必须已经锁住了mutex。函数pthread_cond_timedwait()会对mutex进行【解锁和执行对条件的等待】(原子操作)。这里的原子意味着:解锁和执行条件的等待是原则的,一体的。(In this case, atomically means with respect to the mutex and the condition variable and other access by threads to those objects through the pthread condition variable interfaces.)

如果等待条件满足或超时,或线程被取消,调用线程需要在线程继续执行前先自动锁住mutex,如果没有锁住mutex,产生EPERM错误。即,该函数返回时,mutex已经被调用线程锁住。

即:

pthread_cond_t条件变量必须与互斥锁一起使用,调用pthread_cond_wait系列时,其会自动解锁mutex并执行条件等待(原子操作),而在条件满足或超时等其他原因线程可以继续执行时,会在线程执行前先锁住mutex然后该函数才返回。

在线程等待条件变量时,mutex实际上是处于解锁状态的

当然了,比较悲剧的是,若没锁住则会返回EPERM


下面摘一下别人的例子,我试着编译运行了下,确实如此。。。



?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
 
pthread_t  thread ;
pthread_cond_t cond;
pthread_mutex_t mutex;
int  flag = 1;
 
void  * thr_fn( void  * arg) {
   struct  timeval now;
   struct  timespec outtime;
   pthread_mutex_lock(&mutex);
   while  (flag) {
       printf ( ".\n" );
       gettimeofday(&now, NULL);
       outtime.tv_sec = now.tv_sec + 5;
       outtime.tv_nsec = now.tv_usec * 1000;
       pthread_cond_timedwait(&cond, &mutex, &outtime);
     }
   pthread_mutex_unlock(&mutex);
   printf ( "thread exit\n" );
}
 
int  main() {
   pthread_mutex_init(&mutex, NULL);
   pthread_cond_init(&cond, NULL);
   if  (0 != pthread_create(& thread , NULL, thr_fn, NULL)) {
       printf ( "error when create pthread,%d\n" errno );
       return  1;
     }
   char  c ;
   while  ((c =  getchar ()) !=  'q' );
   printf ( "Now terminate the thread!\n" );
   flag = 0;
   pthread_mutex_lock(&mutex);
   pthread_cond_signal(&cond);
   pthread_mutex_unlock(&mutex);
   printf ( "Wait for thread to exit\n" );
   pthread_join( thread , NULL);
   printf ( "Bye\n" );
   return  0;
}
 
    

你可能感兴趣的:(linux下的多线程编程备忘)