Linux进程与线程
Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。支持多用户、多任务、支持多线程和多CPU的操作系统。
进程——资源分配的最小单位,线程——CPU调度(执行程序)的最小单位,同一进程内的线程共享进程的地址空间。
为什么需要多线程处理?与多进程相比有何优势?
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
一与多进程相比,多线程处理是一种非常"节俭"的多任务操作方式。
在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右。
二与多进程相比,多线程处理具有方便的通信机制。
由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,所以声明有static的数据会给多线程程序带来灾难性的打击。
当然Linux平台已经有成熟的pthread 库定义了创建和操纵线程的一套API:
pthread的库并不是linux系统的库,所以在进行编译的时候要加上 -lpthread
gcc filename -lpthread //默认情况下gcc使用c库,要使用额外的库要这样选择使用的库
线程创建``
#include <pthread.h>
//创建成功返回0,否则返回错误编号
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。
attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。
新创建的线程从start_rtn函数的地址开始运行;
注意该函数的类型已被定死了,输入参数和返回类型都是(void *)且该函数只有后面那个无类型指针参数arg。
如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
线程等待
int pthread_join(pthread_t thread, void **rval_ptr);
//若成功返回0,否则返回错误编号,一般把rval_ptr置为NULL。
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。
线程退出
//rval_ptr参数与之前函数对应
int pthread_exit(void *);
获取自身线程的ID
pthread_t pthread_self();
多线程同步
当在一定的时间内只允许某一个线程访问某个资源(进程共享资源),不允许其它的线程访问该资源,就需要用到多线程同步。
线程互斥锁(mutex):
互斥量从本质上说就是一把锁, 提供对共享资源的保护访问权。互斥锁只有两种状态:锁定和非锁定。
int x; // 进程中的全局变量
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex
pthread_mutex_lock(&mutex); // 给互斥体变量加锁
… //对变量x (共享资源)的操作,临界区
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁
1.初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//利用宏 静态初始化
//调用初始化函数动态初始化,参数attr设置为NULL,采用默认的属性初始化。
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。局部变量应采用动态初始化。
2.加锁操作
int pthread_mutex_lock(pthread_mutex_t mutex); //加锁,阻塞
int pthread_mutex_trylock(pthread_mutex_t mutex); //尝试加锁,非阻塞
直接上锁将会堵塞住其他线程,直到当前线程释放该互斥锁。而调用trylock,就要看互斥量有没有处于未锁住状态,如果没有将锁住互斥量,不会出现阻塞并返回0,否则trylock就会失败,不能锁住互斥量,返回EBUSY。
3.解锁操作
int pthread_mutex_unlock(pthread_mutex_t mutex);//解开这个锁
4.销毁锁
int pthread_mutex_destroy(pthread_mutex_t mutex);//销毁这个锁
条件变量:
条件变量用在某个线程需要在某种条件才去保护它将要操作的临界区的情况,从而避免了线程不断轮询检查该条件是否成立而降低效率。
条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。
1.静态初始化一个条件变量
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//宏静态初始化
2.初始化函数动态初始化 条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
通常没啥特殊要求这个attr也设置为NULL,表示采用默认的属性初始化。
3.阻塞等待一个条件变量
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
4.限时等待一个条件变量
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
timeout指定等待的时间,Linux系统的timespec 时间结构体。
4.唤醒等待在这个条件变量上的某个线程(条件已满足)
int pthread_cond_signal(pthread_cond_t cond);
5.唤醒等待在这个条件变量上的全部线程(条件已满足)
int pthread_cond_broadcast(pthread_cond_t cond);
6.销毁一个条件变量
int pthread_cond_destroy(pthread_cond_t cond);
互斥锁与条件变量配合使用:
等待线程:
pthread_cond_wait前要先加锁
pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
pthread_cond_wait被激活后会再自动加锁
激活线程:
加锁(和等待线程用同一个锁)
pthread_cond_signal发送信号
解锁
cond_test.c
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
//宏静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int count = 0;
void *decrement(void *arg) {
printf("in derement.\n");
pthread_mutex_lock(&mutex);
if (count == 0)
pthread_cond_wait(&cond, &mutex);
count--;
printf("----decrement:%d.\n", count);
printf("out decrement.\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
void *increment(void *arg) {
printf("in increment.\n");
pthread_mutex_lock(&mutex);
count++;
printf("----increment:%d.\n", count);
if (count != 0)
pthread_cond_signal(&cond);
printf("out increment.\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t tid_in, tid_de;
pthread_create(&tid_de, NULL, (void*)decrement, NULL);
sleep(2);
pthread_create(&tid_in, NULL, (void*)increment, NULL);
sleep(5);
pthread_join(tid_de, NULL);
pthread_join(tid_in, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
./a.out
cxx@ubuntu16:~/git_proj/c/9_linux_api$ gcc cond_test.c -lpthread
cxx@ubuntu16:~/git_proj/c/9_linux_api$ ./a.out
in derement.
in increment.
----increment:1.
out increment.
----decrement:0.
out decrement.
cxx@ubuntu16:~/git_proj/c/9_linux_api$
demo2.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct node {
int n_number;
struct node *n_next;
} *head = NULL;
/*[thread_func]*/
static void cleanup_handler(void *arg)
{
printf("Cleanup handler of second thread.\n");
free(arg);
(void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{
struct node *p = NULL;
pthread_cleanup_push(cleanup_handler, p);
while (1)
{
pthread_mutex_lock(&mtx);
while (head == NULL){
pthread_cond_wait(&cond, &mtx);
}
p = head;
head = head->n_next;
printf("Got %d from front of queue\n", p->n_number);
free(p);
pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁
}
pthread_cleanup_pop(0);
return 0;
}
int main(void)
{
pthread_t tid;
int i;
struct node *p;
pthread_create(&tid, NULL, thread_func, NULL);
/*[tx6-main]*/
for(i = 0; i < 10; i++)
{
p = malloc(sizeof(struct node));
p->n_number = i;
pthread_mutex_lock(&mtx); //加锁,操作head这个临界资源
p->n_next = head;
head = p;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx); //解锁
sleep(1);
}
printf("thread 1 wanna end the line.So cancel thread 2.\n");
pthread_cancel(tid);
pthread_join(tid, NULL);
printf("All done -- exiting\n");
return 0;
}
参考文献: 《UNIX环境高级编程》