在同一进程环境中使用多个线程可以共享所有的进程资源, 而且可以优化程序流程, 使很多工作异步进行;
线程单独拥有的资源有: 线程id, 堆栈, 信号屏蔽字, 一组寄存器的值, errno变量, 调度优先级和策略等;
线程共同拥有的资源有: 进程的所有资源包括程序代码, 全局变量, 堆栈空间, 文件描述符等;
POSIX.1-2001标准中规定的线程接口称为POSIX thread, 或pthreads. 编译时要加-lpthread.
目前的linux内核是以轻量级进程(lightweight process, LWP)的方式实现多线程的.
内核里每个LWP对应用户空间的一个线程, LWP拥有自己的task_struct, 也是一个进程调度单位;
LWP与普通进程的区别是多个LWP共享某些资源, 如: 地址空间, 打开的文件等;
Solaris的线程库就不是一个LWP对应一个用户空间线程, 而是用户空间分时复用数量有限的LWP.
线程控制:
#include <pthread.h>
int pthread_create( pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
void *(*start_rtn)(void), void *restrict arg);
返回值成功为0, 出错返回错误号;
tidp 用于成功时返回tid值;
attr 表示线程的属性, 使用默认的线程属性可以赋NULL即可;
start_rtn 线程回调函数名称;
arg 给回调函数传参数, 一般是自定义的结构体, 可一次传多项数据;
新线程继承了原线程的上下文和信号屏蔽字, 但原线程的未决信号在新线程中被清除.
如果同一进程的任一线程调用了exit, _exit, 则整个进程的所有线程都终止. 因而, 如果一个信号的处理动作是终止进程, 这样的信号递送到任何一个线程, 整个进程的所有线程都终止;
如果要只终止线程, 三种方法:
1. 从线程函数返回, 返回值是线程的终止状态码, main函数这样处理会终止整个进程;
2. 调用pthread_cancel终止同一进程的另一线程;
3. 调用pthread_exit终止自己;
#include <pthread.h>
void pthread_exit(void *rval_ptr);
rval_ptr 其类型可用户定义, 其空间必须是全局的或是malloc分配的, 不能在线程函数的栈上分配.
同一个进程的其他线程调用pthread_join可获得这个指针;
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr); 返回值成功为0, 出错返回错误号;
调用该函数的线程将挂起等待, 直到对应的线程通过上述三种方法之一返回;
rval_ptr指向的内存单元应由调用者事先分配, 如void * t_result; pthread_join(a_thread, &t_result);
三种返回方式对应的rval_ptr返回的内容是不同的:
1. 从线程函数返回, rval_ptr指向的单元存放的不是指针而是返回值;
2. 被令一线程pthread_cancel导致的返回, 存放的是常数PTHREAD_CANCELED;
3. 线程自己调用pthread_exit返回, 存放的就是pthread_exit(void *rval_ptr)里面的参数;
#include <pthread.h>
int pthread_cancel(pthread_t tid); 返回值成功为0, 出错为错误号;
线程被其他线程cacel掉, 相当于自己调用pthread_exit(PTHREAD_CANCELED).
调用pthread_cancel的线程不必等到tid线程结束, 而只是发出这个请求.
tid线程也可以调整线程属性来忽略pthread_cancel请求.
#include <pthread.h>
int pthread_detach(pthread_t tid); 成功返回0, 失败返回错误号;
线程终止后, 其终止状态一直保留到其他线程调用pthread_join来获取为止.
若线程被detach, 那么线程一旦终止, 资源就立刻被回收, 不保留其终止状态.
若线程已经被detach, 对它调用pthread_join会返回-EINVAL.
线程与进程函数的对比:
进程 线程
fork pthread_create
exit pthread_exit
waitpid pthread_join
abort pthread_cancel
线程间的同步:
多个线程访问同一共享资源时需要一些同步机制来保证共享数据的一致性, 与信号当中的可重入性问题相同;
临界区: 不允许被打断的区域;
读者写者锁reader-writer-lock, reader之间并不互斥, writer是独占的exclusive.
互斥锁mutex用pthread_mutex_t类型的变量表示.
如果静态分配该类型的变量, 可以使用常数PTHREAD_MUTEX_INITIALIZER初始化它 ?
如果调用malloc分配一个mutex, 则应该调用pthread_mutex_init()初始化它;
在调用free释放之前应该调用pthread_mutex_destroy()销毁它.
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
成功返回0, 出错返回错误号;
attr是mutex的属性, 如果使用缺省属性初始化mutex, 传NULL即可;
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex); 尝试获取锁而不挂起等待, 失败返回-EBUZY.
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0, 出错返回错误号;
关于死锁deadlock, 出现的情形有:
1. 同一线程先后两次调用lock. 如: A ->A ->! A ->! A
2. 两个不同线程交叉嵌套用来多把锁. 如: 线程1: A ->B->! B->! A 线程2: B->A->! A->! B
——————————————————————————–
线程的一些例程:
/*thread.c
*a simple thread program with args
*/
#include <pthread.h>
#include <stdio.h>
#define N 1
void *thread(void *arg)
{
printf("hello, world! <from %d>\n", *(int*)arg);
sleep(2);
}
int main()
{
pthread_t tid[N];
int i, arg[N];
while(1)
{
sleep(1);
for (i = 0; i < N; i++)
{
arg[i] = i;
pthread_create(&tid[i], NULL, thread, (void*)&arg[i]);
}
}
/*
for (i = N-1; i >= 0; i–)
{
pthread_join(tid[i], NULL);
printf ("—–%d\n", i);
}*/
return 0;
}
/*threadmut.c
*a program to practice using mutex to lock,
*there is a common data:work_area[],
*which store the data input at main thread,
*and in the thread_function to use it.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0)
{
perror ("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0)
{
perror("Thread creation failed");
exit (EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter ‘end’ to finish\n");
while (!time_to_exit)
{
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1)
{
pthread_mutex_lock(&work_mutex);
if (work_area[0] != ‘\0’)
{
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else
{
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf ("\nWaiting for thread to finish …\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0)
{
perror ("Thread join failed");
exit (EXIT_FAILURE);
}
printf ("Thread joined\n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0)
{
printf ("Yor input %d characters\n",
strlen(work_area) – 1);
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == ‘\0’)
{
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = ‘\0’;
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
/*queue.c
*a simple program to realize a queue
*/
#include <stdio.h>
#include <stdlib.h>
#define N 16
int Q[N];
int front, rear;
void queue_init(int n)
{
front = rear = 0;
}
void queue_destroy()
{
front = rear = 0;
}
void enque(int item)
{
Q[rear++] = item;
rear %= N;
}
int deque()
{
int item = Q[front++];
front %= N;
return item;
}
int main()
{
int i;
queue_init(N);
for (i=0; i<N; i++) enque(i);
for (i=0; i<N; i++) printf("%3d", deque());
puts("");
return 0;
}
/* Parallel C code to demonstrate Linux thread interface */
/* Algorithm appears to be based on the series expansion of arctan(pi/4) */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
volatile double pi = 0.0; /* Approximation to pi (shared) */
pthread_mutex_t pi_lock; /* Lock for above */
volatile double intervals; /* How many intervals? */
void *process(void *arg)
{
register double width, localsum;
register int i;
register int iproc = (*((char *) arg) – ‘0’);
/* Set width */
width = 1.0 / intervals;
/* Do the local computations */
localsum = 0;
for (i=iproc; i<intervals; i+=2)
{
register double x = (i + 0.5) * width;
localsum += 4.0 / (1.0 + x * x);
}
localsum *= width;
/* Lock pi for update, update it, and unlock */
pthread_mutex_lock(&pi_lock);
pi += localsum;
pthread_mutex_unlock(&pi_lock);
return(NULL);
}
int main(int argc, char **argv)
{
pthread_t thread0, thread1;
void * retval;
/* Get the number of intervals */
intervals = atoi(argv[1]);
/* Initialize the lock on pi */
pthread_mutex_init(&pi_lock, NULL);
/* Make the two threads */
pthread_create(&thread0, NULL, process, "0");
pthread_create(&thread1, NULL, process, "1");
/* Join (collapse) the two threads */
pthread_join(thread0, &retval);
pthread_join(thread1, &retval);
/* Print the result */
printf("Estimation of pi is %20.18f\n", pi);
return 0;
}
/*pd.c
*a program about philo eating
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t chopstick[5];
void *philosopher(void *arg)
{
int p = *(int*)arg;
while (1) {
pthread_mutex_lock(&chopstick[p]);
pthread_mutex_lock(&chopstick[(p+1)%5]);
printf("philosopher %d eating…\n", p);
sleep(1);
pthread_mutex_unlock(&chopstick[p]);
pthread_mutex_unlock(&chopstick[(p+1)%5]);
printf("philosopher %d thinking…\n", p);
sleep(1);
}
}
int main()
{
int i, p[5];
pthread_t pid[5];
for (i=0; i<5; i++)
pthread_mutex_init(&chopstick[i], NULL);
for (i=0; i<5; i++) {
p[i] = i;
pthread_create(&pid[i], NULL,
philosopher, (void*)&p[i]);
}
for (i=0; i<5; i++)
pthread_join(pid[i], NULL);
return 0;
}
/*prodcom.c
*producer & consumer
*/
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
sem_t empty, full; // the global semaphores
int data; // shared buffer
int num;
// deposit 1, …, num into the data buffer
void *Producer(void *arg)
{
int produced;
printf("Producer created\n");
for (produced = 1; produced <= num; produced++) {
sem_wait(&empty);
data = produced;
sem_post(&full);
}
}
// fetch num items from the buffer and sum them
void *Consumer(void *arg)
{
int total = 0, consumed;
printf("Consumer created\n");
for (consumed = 0; consumed < num; consumed++) {
sem_wait(&full);
total = total+data;
sem_post(&empty);
}
printf("for %d iterations, the total is %d\n", num, total);
}
int main(int argc, char *argv[])
{
pthread_t pid, cid;
num = atoi(argv[1]);
sem_init(&empty, 0, 1); // sem empty = 1
sem_init(&full, 0, 0); // sem full = 0
printf("main started\n");
pthread_create(&pid, NULL, Producer, NULL);
pthread_create(&cid, NULL, Consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
printf("main done\n");
}
/*q_prodom.c
*a program about productor and cosummer
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#define N 8
#define PN 1
#define CN 2
sem_t empty, full;
int num;
int Q[N], front, rear;
void queue_init(int n) { front = rear = 0; }
void enque(int item)
{
printf("producer%5d\n", rear);
Q[rear++] = item;
rear %= N;
}
int deque()
{
printf("consumer%5d\n", front);
int item = Q[front++];
front %= N;
return item;
}
void *Producer(void *arg)
{
int p = *(int*)arg;
while(1){
sleep(1);
sem_wait(&empty);
printf("%d ", p);
enque(1);
sem_post(&full);
}
}
void *Consumer(void *arg)
{
int c = *(int*)arg;
while (1) {
// sleep(1);
sem_wait(&full);
printf("%d ", c);
deque();
sem_post(&empty);
}
}
int main(int argc, char *argv[])
{
int i, p[PN], c[CN];
pthread_t pid[PN], cid[CN];
sem_init(&empty, 0, N); // sem empty = 1
sem_init(&full, 0, 0); // sem full = 0
for(i=0; i<PN; i++) {
p[i] = i;
pthread_create(
&pid[i], NULL, Producer, (void*)&p[i]);
}
for(i=0; i<CN; i++) {
c[i] = i;
pthread_create(
&cid[i], NULL, Consumer, (void*)&c[i]);
}
for(i=0; i<PN; i++)
pthread_join(pid[i], NULL);
for(i=0; i<CN; i++)
pthread_join(cid[i], NULL);
return 0;
}
——————————————————————————–
信息点滴:
ulimit -c 1024 允许core文件最大为1024K字节
kill -SIGSEGV pid 向进程发送一个SIGSEGV信号
alarm(sec) 发送SIGALRM信号, 默认动作是结束进程;
^C 终止进程 ^\ 终止进程并产生core
^z 暂停运行,无法直接kill掉, 而要kill -9 pid才行
fg [jobs_num] 调到前台运行 bg [jobs_num] 把挂起进程放到后台运行
直接在程序名后面加&运行 相当于 运行+^z挂起+bg后台运行的效果
运行起来后就可以直接kill掉了.
side effect 副作用, 有些函数说明里要有此项.
一般终端输入带有\n结尾, 接受字符串时要考虑这一点.
gdb ->set follow-fork-mode [child | parent ]调试模式设定;
register int i; 用寄存器而不用内存, 速度快.
信号量, PV操作. 原语操作, 不允许被打断的操作.
sem_init(&empty, 0, 1); //empty=1
sem_init(&full, 0, 0); //full=0
sem_wait(&empty); //P- 若empty<1在此等候
sem_post(&full); //V+
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/niulin/archive/2009/08/11/4436088.aspx