文章目录
  1. 1. 一.Linux线程与进程的对比
    1. 1.1. 用户空间角度:
    2. 1.2. 内核空间角度:
    3. 1.3. 总的说来
  2. 2. 二.线程基本操作
  3. 3. 三.线程私有数据
  4. 4. 四.线程同步方法
    1. 4.1. 4.1互斥锁mutex
    2. 4.2. 4.2读写锁
    3. 4.3. 4.3条件变量
  5. 5. 五.线程和信号
  6. 6. 六.线程属性控制

一.Linux线程与进程的对比

用户空间角度:

  1. 新进程创建时,申请独立的地址空间,包括堆、栈、代码段、数据段、BSS段等。并初始化为父进程的值。此后父子进程不能直接互相访问这些资源。
  2. 新线程创建时,只申请独立线程栈,与同进程的其它线程共享进程地址空间,包括的代码段、数据段、BSS段、堆、打开的库、mmap映射的文件、共享内存空间。

内核空间角度:

  1. Linux并不区分进程和线程。创建线程和进程时,都会创建新的PCB。
  2. 不同的是,新进程PCB的mm_struct会创建新地址空间;新线程PCB的mm_struct指向主进程的地址空间,与它共享。

总的说来

进程是OS管理资源的基本单元;线程是Linux系统调度的基本单元。

二.线程基本操作

头文件:pthread.h
由于是POSIX线程库,gcc编译时要加上-pthread选项。

int pthread_create(p_tid,attr,func,arg);
创建线程。新线程从func处开始执行,arg为传递给func的参数。

pthread_t pthead_self();
获取线程自身tid。

pthread_exit(void *ptr);
线程由自身退出,返回信息放在ptr里。

int pthread_join(tid, void **ptr);
等待线程tid退出。ptr存放tid退出时返回信息。

int pthread_detach(tid);
使线程tid进入分离状态。即线程自身独立出来。

int pthread_cancel(tid);
取消同进程中的tid线程。它提出这个请求后立即返回。tid在可取消点的时候退出。

void pthread_cleanup_push(func,arg);
void pthread_cleanup_pop(int execute);
设置线程退出时需要调用的函数。

一个代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *thread_func(void *arg)
{
printf("msg from main:%s\n",(char *)arg);
printf("thread id:%u\n",(unsigned)pthread_self());
pthread_exit("I am thread,exited");
}

int main(int argc,char *argv[])
{

pthread_t tid;
void *thread_ret;
pthread_create(&tid,NULL,thread_func,"I am main");
pthread_join(tid,&thread_ret);
printf("msg from thread:%s\n",(char *)thread_ret);
return 0;
}

运行结果:

1
tao@taohi-xubuntu:~/linux_c$ ./pthread_exit
msg from main:I am main
thread id:3075636032
msg from thread:I am thread,exited

三.线程私有数据

一个全局变量,如果A线程修改了它,那么这个修改会在B线程可见。
为了使得线程有私有全局变量,有如下接口:
pthread_key_create(key,func);
pthread_key_delete(key);
pthread_setspecific(key,ptr);
pthread_getspecific(key);

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

pthread_key_t key;

void *tid1_func(void *arg)
{
int i=10;
printf("set key:%d in tid1\n",i);
pthread_setspecific(key,&i);
sleep(2);
printf("in tid1,after tid2 ends,key:%d\n",*(int *)pthread_getspecific(key));
}

void *tid2_func(void *arg)
{
int j=20;
printf("set key:%d in tid2\n",j);
pthread_setspecific(key,&j);
printf("in tid2,key:%d\n",*(int *)pthread_getspecific(key));
}

void destruct(void *t)
{

printf("key addr:%p\n",t);
}

int main(int argc,char *argv[])
{

pthread_t tid1,tid2;
pthread_key_create(&key,destruct);
pthread_create(&tid1,NULL,tid1_func,NULL);
pthread_create(&tid2,NULL,tid2_func,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_key_delete(key);
return 0;
}

运行结果:

1
2
3
4
5
6
7
tao@taohi-xubuntu:~/linux_c$ ./pthread_key
set key:10 in tid1
set key:20 in tid2
in tid2,key:20
key addr:0xb6d8934c
in tid1,after tid2 ends,key:10
key addr:0xb758a34c

四.线程同步方法

线程并发访问共享资源,带来不确定性。所以需要同步机制。

4.1互斥锁mutex

互斥锁是二元变量。线程只有获得锁,才能操作临界资源。操作完释放锁。
造成死锁原因:

  1. 线程对同一个互斥锁加锁两次;
  2. A拥有甲锁,等乙锁,同时B拥有乙锁,等甲锁。

使用接口:
pthread_mutex my_mutex;
pthread_mutex_init(&my_mutex,NULL);
pthread_mutex_lock(&my_mutex); 试着加锁,锁不上就阻塞等待加锁。
pthread_mutex_trylock(&my_mutex); 试着加锁,锁不上就返回
pthread_mutex_unlock(&my_mutex);
pthread_mutex_destroy(&my_mutex);

4.2读写锁

读写锁比互斥锁并发度更高:
如果A线程获得写锁:其他线程不能获得任何锁;
如果A线程获得读锁:其他线程可以获得读锁,不能获得写锁。

使用接口:
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock,NULL);
pthread_rwlock_destroy(&rwlock);
pthread_rwlock_rdlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
pthread_rwlock_unlock(&rwlock);

4.3条件变量

默默忽略

五.线程和信号

  1. 每个线程有单独的信号屏蔽集合。
  2. 对同一个信号,所有线程共享相同的信号处理程序。比如线程1注册了SIGUSR1的处理函数,那么线程2收到SIGUSR1也会去调用这个处理函数。
  3. 如果一个线程收到终止信号,其他所有线程都将终止。

线程操作接口:
pthread_kill(tid,signo);
向线程tid发送signo信号。

pthread_procmask(how,mask,oldmask);
设置线程信号屏蔽集合。

sigwait()等待一个或者多个信号发生。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

void sig_handler(int signo)
{

printf("tid=%u,sig=%d\n",(unsigned int)pthread_self(),signo);
}

void *tid1_func(void *arg)
{
int i;
sigset_t set;
signal(SIGUSR1,sig_handler);
sigfillset(&set);
sigdelset(&set,SIGUSR2);
pthread_sigmask(SIG_SETMASK,&set,NULL);
for(i=0;i<5;i++)
{
printf("tid1=%u, set mask.\n",(unsigned int)pthread_self());
pause();
}
}

void *tid2_func(void *arg)
{
int i;
signal(SIGUSR2,sig_handler);
for(i=0;i<5;i++)
{
printf("tid2=%u, not set mask.\n",(unsigned int)pthread_self());
pause();
}
}

int main(int argc ,char *argv[])
{

pthread_t tid1,tid2;
pthread_create(&tid1,NULL,tid1_func,NULL);
pthread_create(&tid2,NULL,tid2_func,NULL);
sleep(1);
pthread_kill(tid1,SIGUSR1);
sleep(1);
pthread_kill(tid1,SIGUSR1);
pthread_kill(tid1,SIGUSR2);
pthread_kill(tid2,SIGUSR1);
pthread_kill(tid2,SIGUSR2);
sleep(1);
pthread_kill(tid1,SIGKILL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}

运行结果:

1
tao@taohi-xubuntu:~/linux_c$ ./pthread_signal  
tid1=3076352832, set mask.         
tid2=3067960128, not set mask. 
tid=3076352832,sig=12  
tid=3067960128,sig=12  
tid=3067960128,sig=10  
tid2=3067960128, not set mask.
tid1=3076352832, set mask.
已杀死

六.线程属性控制

int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
获取线程栈大小。通过stacksize指针返回。

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
设置线程栈大小。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void *tid_func(void *arg)
{
pthread_attr_t attr;
size_t stack_size=0;
pthread_attr_getstacksize(&attr,&stack_size);
printf("default pthread stack size:%d KB\n",stack_size/1024);
stack_size=4*1024*1024;
pthread_attr_setstacksize(&attr,stack_size);
pthread_attr_getstacksize(&attr,&stack_size);
printf("after modify,stack size:%d KB\n",stack_size/1024);
pthread_exit(NULL);
}

int main(int argc ,char *argv[])
{

pthread_t tid;
pthread_create(&tid,NULL,tid_func,NULL);
pthread_join(tid,NULL);
return 0;
}

输出结果:

1
2
3
tao@taohi-xubuntu:~/linux_c$ ./pthread_stack
default pthread stack size:8192 KB
after modify,stack size:4096 KB

可以看到linux下线程默认栈大小为8M

文章目录
  1. 1. 一.Linux线程与进程的对比
    1. 1.1. 用户空间角度:
    2. 1.2. 内核空间角度:
    3. 1.3. 总的说来
  2. 2. 二.线程基本操作
  3. 3. 三.线程私有数据
  4. 4. 四.线程同步方法
    1. 4.1. 4.1互斥锁mutex
    2. 4.2. 4.2读写锁
    3. 4.3. 4.3条件变量
  5. 5. 五.线程和信号
  6. 6. 六.线程属性控制