线程与进程关系
-
轻量级进程(light-weightprocess),也有PCB,创建线程使用底层函数和进程一样,都是clone.
-
从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表示相同的
-
进程可以蜕变成线程
-
在美国人眼里,线程就是寄存器和栈
-
在Linux下,线程是最小的执行单位;进程是最小的分配资源的单位
进程原语 | 线程原语 | 描述 |
---|---|---|
fork | pthread_create | 创建新的控制流 |
exit | pthread_exit | 从现有的控制流中退出 |
waitpid | pthread_join | 从控制流中得到退出状态 |
atexit | pthread_cancel_push | 注册在退出控制流时调用的函数 |
getpid | pthread_self | 获取控制流的ID |
abort | phtead_cancle | 请求控制流的正常退出 |
查看线程号
$ps -eLf
UID PID PPID LWP C NLWP STIME TTY TIME CMD
root 1 0 1 0 1 May17 ? 00:00:02 /sbin/init
root 2 0 2 0 1 May17 ? 00:00:00 [kthreadd]
root 3 2 3 0 1 May17 ? 00:00:16 [ksoftirqd/0]
root 5 2 5 0 1 May17 ? 00:00:00 [kworker/0:0H]
root 7 2 7 0 1 May17 ? 00:01:55 [rcu_sched]
LWP列表示的就是线程号
查看具体线程信息
$ps -Lf 7
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
root 7 2 7 0 1 May17 ? S 1:57 [rcu_sched]
线程的本质
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过 一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程, 并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。 这样,原先进程中的变量变动在copy后的进程中便能体现出来。
pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。
/**********************************************************
* Author : yxmsw2007
* Email : yxmsw2007@gmail.com
* Last modified : 2015-05-19 22:34
* Filename : thread_example.c
* Description : gcc -lpthread thread_example.c -o thread_example
* *******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#define MAX 3
int number = 0;
pthread_t id[2];
pthread_mutex_t mut; //初始化静态互斥锁
void thread1(void) {
int i;
printf("Hello, I am pthread1!\n");
for (i = 0; i < MAX; i++)
{
pthread_mutex_lock(&mut); //此处上锁,保证number的唯一性
number++;
printf("THread1: number = %d\n", number);
pthread_mutex_unlock(&mut);
sleep(1); //linux c下sleep(minute),里面单位是分钟
}
pthread_exit(NULL); //线程通过执行此函数,终止执行。返回是一个空指针类型
}
void thread2(void) {
int i;
printf("Hello, I am pthread2!\n");
for (i = 0; i < MAX; i++)
{
pthread_mutex_lock(&mut); //此处上锁,保证number的唯一性
number++;
printf("THread2: number = %d\n", number);
pthread_mutex_unlock(&mut);
sleep(1); //linux c下sleep(minute),里面单位是分钟
}
pthread_exit(NULL); //线程通过执行此函数,终止执行。返回是一个空指针类型
}
void thread_create(void) {
int temp;
memset(&id, 0, sizeof(id));
if (temp = pthread_create(&id[0], NULL, (void*)thread1, NULL) != 0)
{
printf("Thread1 fail to create!\n");
} else {
printf("Thread 1 create!\n");
}
if (temp = pthread_create(&id[1], NULL, (void*)thread2, NULL) != 0)
{
printf("Thread 2 fail to create!\n");
} else {
printf("Thread 2 create!\n");
}
}
void thread_wait() {
if (id[0] != 0)
{
pthread_join(id[0], NULL);
printf("Thread 1 completed!\n");
}
if (id[1] != 0)
{
pthread_join(id[1], NULL);
printf("Thread 2 completed!\n");
}
}
int main(int argc, char *argv[])
{
int i, ret1, ret2;
pthread_mutex_init(&mut, NULL); //动态互斥锁
printf("Main function, creating thread...\n");
thread_create();
printf("Main function, waiting for the pthread end!\n");
thread_wait();
// 代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。
// 加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
return (0);
}
线程退出
单个线程可以通过三种方式退出,在不终止整个进程的情况下停止它的控制流。
(1)线程只是从启动全程中返回(return方式),返回值是线程的退出码。
(2)线程可以被同一进程中的其他线程取消
(3)线程调用pthread_exit
pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
retval:调用者线程的返回值,可由其他函数如pthread_join来检索获取。
pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
thread: 等待线程的标识符
thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)
pthread_cancel
#include <pthread.h>
int pthread_cancel(pthread_t thread);
pthread_setcancelstate与pthread_setcanceltype
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);
/**********************************************************
* Author : yxmsw2007
* Email : yxmsw2007@gmail.com
* Last modified : 2015-05-20 00:23
* Filename : pthread_cancel_setcancelstate.c
* Description : gcc -lpthread pthread_cancel_setcancelstate.c -o pthread_cancel_setcancelstate
* *******************************************************/
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void *thread_func(void *ignored_argument)
{
int s;
/* Disable cancellation for a while, so that we don't
* immediately react to a cancellation request */
s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");
printf("thread_func(): started; cancellation disabled\n");
sleep(5);
printf("thread_func(): about to enable cancellation\n");
s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");
/* sleep() is a cancellation point */
sleep(1000); /* Should get canceled while we sleep */
/* Should never get here */
printf("thread_func(): not canceled!\n");
return NULL;
}
int main(void)
{
pthread_t thr;
void *res;
int s;
/* Start a thread and then send it a cancellation request */
s = pthread_create(&thr, NULL, &thread_func, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
sleep(2); /* Give thread a chance to get started */
printf("main(): sending cancellation request\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
/* Join with thread to see what its exit status was */
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("main(): thread was canceled\n");
else
printf("main(): thread wasn't canceled (shouldn't happen!)\n");
exit(EXIT_SUCCESS);
}
pthread_cleanup_push与pthread_cleanup_pop
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
清理函数rtn的调用顺序是由pthread_clean_push函数安排的。
它在下列几种情况下执行:
-
调用pthread_exit时
-
响应取消请求时
-
用非零execute参数调用pthread_clean_pop时
如果execute参数为0,清理函数将不被调用。无论何种情况,pthread_clean_pop都将删除上次pthread_clean_push建立的清理处理程序。
如果线程使用return从例程返回,那么pthread_clean_push建立的清理处理程序不会被执行。
/**********************************************************
* Author : yxmsw2007
* Email : yxmsw2007@gmail.com
* Last modified : 2015-05-20 00:23
* Filename : pthread_cleanup_push_pop.c
* Description : gcc -lpthread pthread_cleanup_push_pop.c -o pthread_cleanup_push_pop
* *******************************************************/
#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static int done = 0;
static int cleanup_pop_arg = 0;
static int cnt = 0;
static void cleanup_handler(void *arg)
{
printf("Called clean-up handler\n");
cnt = 0;
}
static void *thread_start(void *arg)
{
time_t start, curr;
printf("New thread started\n");
pthread_cleanup_push(cleanup_handler, NULL);
curr = start = time(NULL);
while (!done) {
pthread_testcancel(); /* A cancellation point */
if (curr < time(NULL)) {
curr = time(NULL);
printf("cnt = %d\n", cnt); /* A cancellation point */
cnt++;
}
}
pthread_cleanup_pop(cleanup_pop_arg);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thr;
int s;
void *res;
s = pthread_create(&thr, NULL, thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
sleep(2); /* Allow new thread to run a while */
if (argc > 1) {
if (argc > 2)
cleanup_pop_arg = atoi(argv[2]);
done = 1;
} else {
printf("Canceling thread\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
}
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("Thread was canceled; cnt = %d\n", cnt);
else
printf("Thread terminated normally; cnt = %d\n", cnt);
exit(EXIT_SUCCESS);
}
源码下载
参考资料
线程概念:线程和进程之间的关系,线程间可共享资源,线程间非共享资源,线程的优缺点