多线程
Hello world线程
-
线程创建
pthread_create()
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); /* 一个线程变量名,被创建线程的标识 线程的属性指针,缺省为NULL即可 被创建线程的程序代码 程序代码的参数,For example: - pthread_t thrd1; - pthread_attr_t *attr; - void thread_function(void *argument); - char *some_argument; */
-
线程结束
pthread_exit()
pthread_exit(void *retval); // pthread_exit函数唯一的参数value_ptr是函数的返回代码,只要pthread_join中的第二个参数value_ptr不是NULL,这个值将被传递给value_ptr。
-
线程等待
pthread_join()
int pthread_join( pthread_t thread, void * * value_ptr ); // 阻塞函数,调用它的函数将一直等到被等待的线程结束为止。如果value_ptr不为NULL,那么线程thread的返回值存储在该指针指向的位置。该返回值可以是由pthread_exit给出的值,或者该线程被取消而返回PTHREAD_CANCELED。
实例
#include <stdio.h>
#include <pthread.h>
void *print_msg_thread(void *ptr);
int main(void)
{
pthread_t t1, t2;
char msg1[] = "One", msg2[] = "Two";
if (pthread_create(&t1, NULL, print_msg_thread,msg1) == 0){
printf("线程1创建成功\n");
}else{
printf("线程1创建失败");
}
if (pthread_create(&t2, NULL, print_msg_thread,msg2) == 0){
printf("线程2创建成功\n");
}else{
printf("线程2创建失败");
}
void *retval;
int tmp1, tmp2;
tmp1 = pthread_join(t1, &retval);
printf("thread1 return value(retval) %p\n", retval);
printf("thread1 return value(tmp) is %d\n", tmp1);
printf("thread1 end\n");
tmp2 = pthread_join(t2, &retval);
printf("thread2 return value(retval) %p\n", retval);
printf("thread2 return value(tmp) is %d\n", tmp2);
printf("thread2 end\n");
return 0;
}
void *print_msg_thread(void *ptr)
{
for (int i = 0; i < 5; i++)
{
printf("%s: %d\n", ptr, i);
}
}
多线程的同步与互斥
锁
-
创建
-
静态创建: POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
动态创建: pthread_mutex_init()函数来初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
mutuex用于指定锁的属性
互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:
Name 简述 介绍 PTHREAD_MUTEX_TIMED_NP 缺省值,普通锁 当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。 PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁 允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 PTHREAD_MUTEX_ERRORCHECK_NP 检错锁 如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。 PTHREAD_MUTEX_ADAPTIVE_NP 适应锁 动作最简单的锁类型,仅等待解锁后重新竞争。
-
-
销毁
pthread_mutex_destroy()用于注销一个互斥锁,API定义如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
-
其他
锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
-
死锁
总体来讲, 有几个不成文的基本原则:
- 共享资源操作前一定要获得锁。
- 完成操作以后一定要释放锁。
- 尽量短时间地占用锁。
- 如果有多锁, 如获得顺序是ABC连环扣, 释放顺序应该是CBA。
- 线程错误返回时应该释放它所获得的锁。
信号量
在/usr/include/semaphore.h
中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数
-
初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
返回值: 成功0, 失败-1, errno被设置
参数:
- sem,信号地址
- pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
- value:信号量的初始值
-
信号量操作
int sem_wait(sem_t *sem)
//信号减一,信号 == 1 时,会阻塞int sem_post(sem_t *sem)
//信号+1 -
销毁
int sem_destroy(sem_t *sem);