Linux线程学习总结
2013-03-07 19:57
429 查看
Linux线程学习总结
1.线程基础
1.1.什么是线程
在一个程序里的多个执行路线就叫做线程。更准确的定义是:线程是“一个进程内部的一个控制序列”。典型的unix进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程以后,在程序设计时可以把进程设计成在同一时刻能够做不止一件事,每个线程处理各只独立的任务。1.2. 线程的优点
通过为每种事件类型的处理分配单独的线程,能够简化处理异步时间的代码。多个线程可以自动共享相同的存储地址空间和文件描述符。
有些问题可以通过将其分解从而改善整个程序的吞吐量。
交互的程序可以通过使用多线程实现相应时间的改善,多线程可以把程序中处理用户输入输出的部分与其它部分分开。
1.3. 线程的缺点
线程也有不足之处。编写多线程程序需要更全面更深入的思考。在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的。调试一个多线程程序也比调试一个单线程程序困难得多。2.线程的创建
从第一节我们对线程有了一个基础性的认识,其实线程就是一个轻量级的进程,那么我们应该怎么样创建线程呢?对于新增的线程可以通过pthread_create函数创建。函数如下:Int pthread_create(pthread_t *restrict tidp, Const pthread_attr_t *restrict attr, Void *(*start_rtn)(void *), void *restrict arg); |
#include <pthread.h> #include <stdio.h> void *pthread_func(void *arg) { printf("Hello world!\n"); } int main(int argc, char *argv[]) { int err; pthread_t tid; err = pthread_create(&tid, NULL, pthread_func, NULL); if (err != 0) { printf("Create thread failed: %s\n", strerror(err)); exit(0); } sleep(0); return 0; } |
3.线程终止
如果进程中的任一线程调用exit,__Exit,那么整个进程都会终止,线程可以通过以下方式在不终止整个进程的情况下终止线程的运行a.线程只是从启动里程中返回,返回值是线程的退出码(线程运行完毕)
b.线程可以被同一进程的其他线程取消
c.线程调用pthread_exit退出
#include <pthread.h> Void pthread_exit(void *rval_ptr) |
int pthread_join(pthread_t thread, void **rval_ptr); |
#include <pthread.h> #include <stdio.h> void *pthread_func(void *arg) { printf("Hello world!\n"); pthread_exit(NULL); } int main(int argc, char *argv[]) { int err; pthread_t tid; err = pthread_create(&tid, NULL, pthread_func, NULL); if (err != 0) { printf("Create thread failed: %s\n", strerror(err)); return -1; } pthread_join(tid, NULL); printf("test thread is finished.\n"); return 0; } |
4.线程同步
线程可以通过互斥量、读写锁、条件变量、自旋锁等来控制线程的同步4.1 互斥量
可以通过使用pthread的互斥接口保护数据,确保同时只有一个线程访问数据,互斥量相关的函数有:#include <pthread.h> pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; pthread_mutex_t errchkmutex=PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex); |
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_init(&mut, NULL);/* 返回0表示初始化成功 */ pthread_mutex_lock(&mut); /* * 若想获取不到锁不阻塞,可以使用pthread_mutex_trylock(&mut),返回 * 表示获取锁成功 */ /* operate on x */ pthread_mutex_unlock(&mut); |
4.2 读写锁
读写锁与互斥量类似,不过读写锁允许更高的并行性,多个线程可以进行同时读,如果要保护的数据,大部分操作都是多个线程在读,少量操作是在写,这时候使用读写锁效率会比较高,读写锁主要有如下函数:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); |
4.3 条件变量
条件变量是线程可用的另一种同步机制,条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生,条件变量是由互斥变量保护的,线程在改变条件状态前首先要锁住互斥量,其他线程在获得互斥量之前不会觉察到这种改变,与条件变量相关的函数如下所示:pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_cond_destroy(pthread_cond_t *cond); /* 向线程或者条件发送信号 */ int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); /* 等待条件为真,一个是一直阻塞,一个是有超时时间 */ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); 以上函数的返回值都是:若成功则返回0,否则返回错误编号 |
#include <pthread.h> #include <stdio.h> #include <stdlib.h> typedef struct msg_s { struct msg_s *m_next; /* add more stuff here */ } msg_t; msg_t *workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; void receive_msg(void) { msg_t *mp; while (1) { pthread_mutex_lock(&qlock); while (workq == NULL) { pthread_cond_wait(&qready, &qlock); } printf("%s, The work queue is not empty.\n", __func__); mp = workq; workq = mp->m_next; /* handle the msg */ free(mp); pthread_mutex_unlock(&qlock); } } void send_msg(msg_t *mp) { pthread_mutex_lock(&qlock); mp->m_next = workq; workq = mp; pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready);/* 也可以在解锁前发送 */ } void *pthread_func(void *arg) { receive_msg(); pthread_exit(NULL); } int main(int argc, char *argv[]) { int err, cnt; msg_t *mp; pthread_t tid; err = pthread_create(&tid, NULL, pthread_func, NULL); if (err != 0) { printf("Create thread failed\n"); return -1; } for (cnt = 0; cnt < 10; cnt++) { mp = (msg_t *)malloc(sizeof(msg_t)); if (mp == NULL) { printf("Can't alloc msg memory.\n"); sleep(1); continue; } send_msg(mp); sleep(1); } pthread_join(tid, NULL); printf("test thread finished.\n"); return 0; } |
5.线程属性
前面所有调用pthread_create函数的例子中,传入的参数都是NULL,而不是指向pthread_attr_t结构的指针,可以使用pthread_attr_t结构修改线程的属性,对于线程属性,我在编程过程中基本上都使用默认属性,所以这方面的注意点及使用也不是很了解,倒是有修改过线程的调度策略属性,结果没有修改成功,那为什么会修改不成功呢?修改线程的调用策略属性要超级用户才能修改即root用户,其它用户是不能修改的,而我们设备好像是什么用户都不是将一个线程绑定到固定cpu或者绑定到固定的cpu集合,可以使用pthread_setaffinity_np函数,获取线程绑定cpu的信息使用pthread_getaffinity_np函数,这两个函数如下所示:
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <pthread.h> int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset); int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset); Compile and link with -pthread |
#include<stdlib.h> #include<stdio.h> #include<sys/types.h> #include<sys/sysinfo.h> #include<unistd.h> #define __USE_GNU #include<sched.h> #include<ctype.h> #include<string.h> #include <pthread.h> void *pthread_bind_cpu_test(void *agrv) { int j, i; cpu_set_t mask; cpu_set_t get; int num; num = = sysconf(_SC_NPROCESSORS_CONF); /* 获取cpu核的个数 */ CPU_ZERO(&mask); /* 当然这边也不需要是一个cpu集合,某个cpu也是可以的 */ for (j = 0; j < num; j++) { CPU_SET(j, &mask); } if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) { printf("Can't set CPU affinity...\n"); } printf("%s, %d.\n", __func__, __LINE__); j = 0; while (j++ < 50) { CPU_ZERO(&get); if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) { printf("warning: cound not get cpu affinity, continuing...\n"); } for (i = 0; i < num; i++) { if (CPU_ISSET(i, &get)){ printf("this pthread is running processor : %d\n", i); } } } } int main(int argc, char* argv[]) { int num = sysconf(_SC_NPROCESSORS_CONF); int created_thread = 0; int myid; int i; int j = 0; int ret; pthread_t tid; cpu_set_t mask; cpu_set_t get; if (argc != 2) { printf("usage : ./cpu num\n"); exit(1); } myid = atoi(argv[1]); ret = pthread_create(&tid, NULL, pthread_bind_cpu_test, NULL); if (ret < 0) { printf("%s, create thread failed.\n", __func__); } printf("login = %s.\n", getlogin()); printf("system has %i processor(s). \n", num); CPU_ZERO(&mask); CPU_SET(myid, &mask); /* 对于进程使用下面的函数绑定cpu */ if (sched_setaffinity(0, sizeof(mask), &mask) == -1){ printf("warning: could not set CPU affinity\n"); } while (j++ < 50) { CPU_ZERO(&get); if (sched_getaffinity(0, sizeof(get), &get) == -1) { printf("warning: cound not get cpu affinity\n"); } for (i = 0; i < num; i++) { if (CPU_ISSET(i, &get)){ printf("running processor : %d\n",i); } } } pthread_join(tid, NULL); return 0; } |
sudo apt-get install glibc-doc
sudo apt-get install manpages-posix-dev
如果还想知道线程相关的有什么函数,我们可以查看我们环境下的编译器的头文件,如现在我使用的mips的编译器,找到其安装编译器的目录,具体可以查看每个工程目录下tools文件夹,然后打开XXX-elf文件夹,pthread.h就在其include文件夹里面。线程相关的函数就在这里面,当然这不包括全部的函数,还有一些函数是没有被包含在这里面的,如pthread_setaffinity_np和pthread_getaffinity_np函数。
其它,要把信号发送到进程,可以调用kill函数;要把信号发送到线程,可以调用pthread_kill函数。
#include <pthread.h> #include <signal.h> int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *old‐mask); int pthread_kill(pthread_t thread, int signo); |
相关文章推荐
- Unix/linux进程及线程间同步技术总结【学习总结,请勿吐槽。。。】
- Linux学习总结(八)——线程
- linux学习总结进程与线程——exec函数族
- Unix/linux进程及线程间同步技术总结【学习总结,请勿吐槽。。。】
- linux学习总结进程与线程
- Linux 线程属性函数总结
- Linux 常用命令学习总结
- 学习总结 java线程
- Linux学习之线程封装三:基于模板的面向对象的封装
- Linux命令学习总结(一)
- Linux学习总结(六)——进程
- java线程学习详解、总结!
- 【Linux】线程总结:线程同步 -互斥锁,条件变量,信号量实现多生产者多消费者模型
- Linux下的ELF可执行文件学习总结
- 记 Linux 学习知识总结 -- 寒江老师视频(七) - SHELL 讲解
- Linux 线程知识总结
- linux下菜鸟学习mysql总结
- Linux的进程/线程通信方式总结
- Linux系统分析线上课程学习总结