典型的UNIX/Linux进程可以看成只有一个控制进程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事情,每个线程各自处理独立的任务。
进程是程序执行时的一个实例,是担当分配系统资源(CPU时间,内存等)的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。线程是操作系统能够进行运算调度的最小单位,是进程中的实际运作单位。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉接等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率相对差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
使用线程的理由- 使用多线程与进程相比,是一种非常节俭的多任务操作方式。在Linux系统中,启动一个新进程必须分配给它独立的地址空间,建立众多的数据表来维护他的代码段,堆栈段和数据段,这是一种昂贵的做任务工作方式。而运行一个进程中的多个线程,他们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于一个进程花费的时间,并且线程间彼此切换所需的时间也远远小于进程间切换所需时间。
- 线程间还有方便通信的机制。对不同进程来说,他们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式耗时而且不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其他线程所用,方便快捷。的那数据的共享也有一些问题,有的变量不能同时被两个线程修改,有的子程序中声明为static的数据更有可能给多线程程序造成很大打击。
多线程开发在Linux平台上已经有成熟的pthread库支持。涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作有分线程的创建,退出,等待3种。互斥锁则包括4种操作,分别是创建,销毁,加锁和解锁。条件操作有5种操作:创建,销毁,出发,广播和等待。
#include线程的创建,等待,退出//int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) void *func1(void *arg) { static int param = 10; printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); pthread_exit((void*)(¶m)); } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; ret = pthread_create(&t1, NULL, func1, (void *)¶m); if(ret == 0) { printf("main:creat t1 success!n"); } printf("main:%ldn", (unsigned long)pthread_self()); // while(1); pthread_join(t1, (void**)&pret); printf("main:t1 quit %dn", *pret); return 0; }
#include线程共享内存空间的代码验证#include //int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) void *func1(void *arg) { static int param = 10; printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); pthread_exit((void*)(¶m)); } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; ret = pthread_create(&t1, NULL, func1, (void *)¶m);//当pthread_creat成功返回时,有tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂时可以把它设置为NULL,以创建默认属性的线程 //新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入 if(ret == 0) { printf("main:creat t1 success!n"); } printf("main:%ldn", (unsigned long)pthread_self()); // while(1); pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回或被取消 printf("main:t1 quit %dn", *pret); return 0; }
#include线程同步之互斥量加锁解锁#include //int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) int g_data = 0; void *func1(void *arg) { printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); while(1) { printf("t1:%dn", g_data++); sleep(1); } } void *func2(void *arg) { printf("t2:%ld creat thread successn", (unsigned long)pthread_self()); printf("t2:param is %dn", *((int *)arg)); while(1) { printf("t2:%dn", g_data++); sleep(1); } } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; pthread_t t2; ret = pthread_create(&t1, NULL, func1, (void *)¶m);//当pthread_creat成功返回时,有tidp指向的内存单元被设置为 新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂时可以把它设置为NULL,以创建默认属性的线程 //新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数>不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入 if(ret == 0) { printf("main:creat t1 success!n"); } ret = pthread_create(&t2, NULL, func2, (void *)¶m); if(ret == 0) { printf("main:creat t2 success!n"); } printf("main:%ldn", (unsigned long)pthread_self()); while(1) { printf("main:%dn", g_data++); sleep(1); } pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中> 返回或被取消 pthread_join(t2, (void**)&pret); printf("main:t1 quit %dn", *pret); return 0; }
互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞知道当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥锁加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。
在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以共享资源,那么即使其他的线程在使用共享资源前都获取了锁,也会出现数据不一致的问题。
#includeint pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);//创建互斥锁 //除非要创建一个非默认属性的条件变量,否则attr参数可以设置为NULL int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥锁
#includeint pthread_mutex_lock(pthread_mutex_t *mutex);//加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
#include互斥锁限制共享资源的访问#include //int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) int g_data = 0; pthread_mutex_t mutex; void *func1(void *arg) { int i = 0; pthread_mutex_lock(&mutex); for(i = 0; i < 5; i++) { printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); sleep(1); } pthread_mutex_unlock(&mutex); } void *func2(void *arg) { pthread_mutex_lock(&mutex); printf("t2:%ld creat thread successn", (unsigned long)pthread_self()); printf("t2:param is %dn", *((int *)arg)); pthread_mutex_unlock(&mutex); } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex, NULL); ret = pthread_create(&t1, NULL, func1, (void *)¶m);//当pthread_creat成功返回时,有tidp指向的内存单元被设置为 新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂时可以把它设置为NULL,以创建默认属性的线程 { printf("main:creat t1 success!n"); } ret = pthread_create(&t2, NULL, func2, (void *)¶m); if(ret == 0) { printf("main:creat t2 success!n"); } printf("main:%ldn", (unsigned long)pthread_self()); pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回>或被取消 pthread_join(t2, (void**)&pret); // printf("main:t1 quit %dn", *pret);//存在会出现段错误 pthread_mutex_destroy(&mutex); return 0; }
#include造成死锁的原因#include //int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) int g_data = 0; pthread_mutex_t mutex; void *func1(void *arg) { printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); pthread_mutex_lock(&mutex); while(1) { printf("t1:%dn", g_data++); sleep(1); if(g_data == 3) { pthread_mutex_unlock(&mutex); printf("t1 quit========================n"); pthread_exit(NULL); // exit(0); } } // pthread_mutex_unlock(&mutex); } void *func2(void *arg) { printf("t2:%ld creat thread successn", (unsigned long)pthread_self()); printf("t2:param is %dn", *((int *)arg)); while(1) { printf("t2:%dn", g_data); pthread_mutex_lock(&mutex); g_data++; pthread_mutex_unlock(&mutex); sleep(1); } } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex, NULL); ret = pthread_create(&t1, NULL, func1, (void *)¶m);//当pthread_creat成功返回时,有tidp指向的内存单元被设置为 新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂时可以把它设置为NULL,以创建默认属性的线程 //新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数>不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入 if(ret == 0) { printf("main:creat t1 success!n"); } ret = pthread_create(&t2, NULL, func2, (void *)¶m); if(ret == 0) { printf("main:creat t2 success!n"); } // printf("main:%ldn", (unsigned long)pthread_self()); while(1) { printf("main:%dn", g_data); sleep(1); } pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回>或被取消 pthread_join(t2, (void**)&pret); pthread_mutex_destroy(&mutex); return 0; }
拥有两把锁或多把锁时会出现死锁的情况。
eg:当有两把锁时,线程1获得锁一的同时想要获得锁二,而线程二获得了锁二的同时想要获得锁一,这样就会出现死锁的情况,导致线程一线程二都不会运行。 #include线程条件控制实现线程的同步#include //int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) int g_data = 0; pthread_mutex_t mutex; pthread_mutex_t mutex2; void *func1(void *arg) { pthread_mutex_lock(&mutex); sleep(1); pthread_mutex_lock(&mutex2); int i = 0; for(i = 0; i < 5; i++) { printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); sleep(1); } pthread_mutex_unlock(&mutex); } void *func2(void *arg) { pthread_mutex_lock(&mutex2); sleep(1); pthread_mutex_lock(&mutex); printf("t2:%ld creat thread successn", (unsigned long)pthread_self()); printf("t2:param is %dn", *((int *)arg)); pthread_mutex_unlock(&mutex); } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex, NULL);//除非要创建一个非默认属性的条件变量,否则attr参数可以设置为NULL pthread_mutex_init(&mutex2, NULL); ret = pthread_create(&t1, NULL, func1, (void *)¶m);//当pthread_creat成功返回时,有tidp指向的内存单元被设置为 新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂时可以把它设置为NULL,以创建默认属性的线程 { printf("main:creat t1 success!n"); } ret = pthread_create(&t2, NULL, func2, (void *)¶m); if(ret == 0) { printf("main:creat t2 success!n"); } printf("main:%ldn", (unsigned long)pthread_self()); pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回>或被取消 pthread_join(t2, (void**)&pret); pthread_mutex_destroy(&mutex); pthread_mutex_destroy(&mutex2); return 0; }
#include//创建及销毁条件变量 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);//除非要创建一个非默认属性的条件变量,否则attr参数可以设置为NULL int pthread_cond_destroy(pthread_cond_t *cond); //等待 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_metux_t *restrict mutex); int pthread_cond_timewait(pthread_cond_t *restrict cond, pthread_metux_t *restrict mutex, cond struct timespec *restrict timeout); //成功返回0,否则返回错误编号 //触发 int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); //成功返回0,否则返回错误编号
Linux线程开发中,通常对于互斥量,条件变量等都会提供两种初始化,分别是动态初始化和静态初始化。
pthread_cond_t cond; //动态初始化:pthread_cond_init(&cond, NULL); //静态初始化:pthread_cond_t = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex; //动态初始化:pthread_mutex_init(&mutex, NULL); //静态初始化:pthread_cond_t = PTHREAD_MUTEX_INITIALIZER;
eg: #include#include pthread_mutex_t mutex; pthread_cond_t cond; int g_data = 0; void *func1(void *arg) { printf("t1:%ld creat thread successn", (unsigned long)pthread_self()); printf("t1:param is %dn", *((int *)arg)); while(1) { pthread_cond_wait(&cond, &mutex); printf("t1 quit========================n"); printf("t1:%dn", g_data); g_data = 0; sleep(1); } } void *func2(void *arg) { printf("t2:%ld creat thread successn", (unsigned long)pthread_self()); printf("t2:param is %dn", *((int *)arg)); while(1) { printf("t2:%dn", g_data); pthread_mutex_lock(&mutex); g_data++; if(g_data == 3) { pthread_cond_signal(&cond); } pthread_mutex_unlock(&mutex); sleep(1); } } int main() { int ret; int param = 10; int *pret = NULL; pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); ret = pthread_create(&t1, NULL, func1, (void *)¶m); if(ret == 0) { // printf("main:creat t1 success!n"); } ret = pthread_create(&t2, NULL, func2, (void *)¶m); if(ret == 0) { // printf("main:creat t2 success!n"); } pthread_join(t1, (void**)&pret);//调用这个函数的线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回>或被取消 pthread_join(t2, (void**)&pret); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }
//测试代码
int main(int argc, char **argv)
{
int time = atoi(argv[1]);
int i;
for(i = 0; i < time; i++)
{
system("./demo8");
}
}
//将执行结果放入test.ret.txt
//执行命令:./a.out 10 >>test.ret.txt &
//杀死进程:ps -aux|grep a.out
kill



