c/c++线程
进程 process
线程 thread
进程和线程都是由操作系统所体会的程序运行的基本单元。
一个程序至少有一个进程,一个进程至少有一个线程。
具体的可以参照网络上的牛人对进程和线程的理解。
http://www.cnblogs.com/flashsky/articles/642720.html
用户空间看,有区别。
每个进程在创建时,除了内核的操作(PCB)外,还需要申请代码段,数据段,bss段,栈,堆空间,并且初始化为父进程空间的值,父子进程之间不能互访资源。所以进程通信,需要OS的内核支持,不灵活,代价大。
每个线程在创建(PCB)的时候,仅仅申请自己的栈空间,而且同进程的其他线程共享地址空间(除了栈)。同进程的各线程间共享数据,通信很方便而且代价小。
linux下,内核角度看,进程和线程无差别(PCB)。OS是基于线程进行调度的。
一个进程如果不创建线程,可以说是只有一个线程的进程。
如果创建新线程,原来的进程亦称为主线程。
POSIX 线程中不存在父子层次关系。
Linux下的POSIX线程
POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX )。
1.创建线程
///成功返回0
/* Create a new thread, starting with execution of START-ROUTINE
getting passed ARG. Creation attributed come from ATTR. The new
handle is stored in *NEWTHREAD. */
extern int pthread_create (pthread_t *__restrict __newthread,
const pthread_attr_t *__restrict __attr,
void *(*__start_routine) (void *),
void *__restrict __arg) __THROWNL __nonnull ((1, 3));
a.线程id 无符号长整型。
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
b.线程属性
union pthread_attr_t
{
char __size[__SIZEOF_PTHREAD_ATTR_T];
long int __align;
};
使用默认时,设为 NULL。
c. __start_routine 开始执行的函数, void *(*) (void *) 类型的函数。
注意类型,c++里对次严格要求,编译时注意。
d. __start_routine 的参数。 void * 类型,万能类型。
任何指针都能自动 转变为 void*。
解析1.
pthread_self() 与 syscall(SYS_gettid) 区别
pthread_self();
/* Obtain the identifier of the current thread. */
extern pthread_t pthread_self (void) __THROW __attribute__ ((__const__));
#include <sys/syscall.h>
syscall(SYS_gettid)
Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型 pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的 (即不同进程中的线程可能有相同的id) 。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP, lightweight process),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。
有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthread id,而只能使用该线程的真实pid,称为tid。
有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。
http://blog.chinaunix.net/uid-28458801-id-4630215.html
判断主线程,线程id == 进程id
程序启动默认主线程在运行,要想有其他线程你必须手动创建,线程之间没用没用主从说法,你要判断线程是不是main线程,可以::syscall(SYS_gettid)==getpid()来判断,判断线程的tid是否等于该进程的id。
http://zhidao.baidu.com/link?url=eadBzDKlNoEIzNHvMB_cow9XdtNKNoF_98Rjhbd5agZJX3AsiVkWZ0-HClDpTXtRTDh_lWGYZiNJTmpIz2fJV
解析2. 编译
pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。
问题解决:
i. gcc/g++ 编译
在编译中要加 -lpthread参数
gcc thread.c -o thread -lpthread
thread.c为你些的源文件,不要忘了加上头文件#include<pthread.h>
ii. codeblocks中
在Project -> Building options -> Linker Setting -> add 加入需要链接的库,curses.h 是 /usr/lib/libcurses.so ,或者直接写curses 和 pthread,就是-l 后面的名字
现在我们知道了,如何创建线程,和新的线程如何执行。
那么主线程如何执行呢? ---顺序执行。
新线程结束时如何处理?
新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或“连接”。
2. 线程退出与等待
2.1线程退出条件:
- 本线程调用 pthread_exit() 。
- 其他线程调用 pthread_cancel() 取消该线程。
- 主进程退出,或者整个函数结束。
- 其中一个线程执行了 exec类 函数执行新的代码,替换当前线程所有地址空间。
- 当前线程代码执行完毕。
///pthread_exit()
/* Terminate calling thread.
The registered cleanup handlers are called via exception handling
so we cannot mark this function with __THROW.*/
extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));
一般情况,为了有效同步子线程,在主线程中都将等待子线程结束,显示的等待某线程结束可以调用 pthread_join()。主线程将中断(转向睡眠),然后等待子线程完成。
///pthread_join()
/* Make calling thread wait for termination of the thread TH. The
exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
is not NULL.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int pthread_join (pthread_t __th, void **__thread_return);
如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。
如果有一个线程称为线程 1,线程 1 创建了称为线程 2 的线程,则线程 1 自己没有必要调用 pthread_join() 来合并线程 2,程序中其它任一线程都可以做到。当编写大量使用线程的代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所有已停止线程的全局“死线程列表”,然后让一个专门的清理线程专等停止的线程加到列表中。这个清理线程调用 pthread_join() 将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处理了全部清理。
2.2线程退出前操作
和atexit()类似。在线程退出前,执行用户显示定义的某些函数。
当某线程申请了临街资源,在线程终止时,能够顺利释放掉所占用的资源。
采用栈结构来管理
pthread_cleanup_push(void (*routine) (void *), void * arg)
pthread_cleanup_pop(int execute)
调用pthread_cleanup_push()时压入清理函数栈,多次调用将形成一个函数链。执行顺序相反。
execute参数,表示执行到pthread_cleanup_pop()时,弹出清理函数的同时是否执行。
0---不执行,
1---执行。
2.3取消线程---取消一个正在执行的线程
条件:
1.线程是否可以被取消,默认可以
2.该线程是否处于取消点
/* Cancel THREAD immediately or at the next possibility. */
extern int pthread_cancel (pthread_t __th);
执行取消操作时,将调用 清理函数栈中的函数,顺序相反。pthread_cancel()调用者不会等待目标线程操作完成。
成功返回0.
/* Set cancelability state of current thread to STATE, returning old
state in *OLDSTATE if OLDSTATE is not NULL. */
extern int pthread_setcancelstate (int __state, int *__oldstate);
设置取消状态
PTHREAD_CANCEL_ENABLE /// 当前线程可被取消
PTHREAD_CANCEL_DISABLE /// 当前线程不可被取消
/* Set cancellation state of current thread to TYPE, returning the old
type in *OLDTYPE if OLDTYPE is not NULL. */
extern int pthread_setcanceltype (int __type, int *__oldtype);
设置取消类型
PTHREAD_CANCEL_DEFERRED /// 默认,到达取消点可取消
PTHREAD_CANCEL_ASYNCHRONOUS /// 随时执行
3.线程与私有数据
Thread-specific Data TSD
现在有一全局变量,所有线程都可以使用它,改变它的值。而如果每个线程希望能单独拥有它,那么就需要使用线程存储了。表面上看起来这是一个全局变量,所有线程都可以使用它,而它的值在每一个线程中又是单独存储的。这就是线程存储的意义。
下面说一下线程存储的具体用法。
3.1 创建一个类型为 pthread_key_t 类型的变量。
/* Keys for thread-specific data */
typedef unsigned int pthread_key_t;
3.2 创建,注销
调用 pthread_key_create() 来创建该变量。该函数有两个参数,第一个参数就是上面声明的 pthread_key_t 变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
/* Functions for handling thread-specific data. */
/* Create a key value identifying a location in the thread-specific
data area. Each thread maintains a distinct thread-specific data
area. DESTR_FUNCTION, if non-NULL, is called with the value
associated to that key when the key is destroyed.
DESTR_FUNCTION is not called if the value associated is NULL when
the key is destroyed. */
/*
以key所关联的数据作为参数
调用其指向的资源释放参数
*/
extern int pthread_key_create (pthread_key_t *__key,
void (*__destr_function) (void *))
__THROW __nonnull ((1));
/* Destroy KEY. */
extern int pthread_key_delete (pthread_key_t __key) __THROW;
3.3 当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void* 变量,这样你可以存储任何类型的值。
/* Store POINTER in the thread-specific data slot identified by KEY. */
extern int pthread_setspecific (pthread_key_t __key,
const void *__pointer) __THROW ;
3.4 如果需要取出所存储的值,调用 pthread_getspecific() 。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void * 类型的值。
/* Return current value of the thread-specific data slot identified by KEY. */
extern void *pthread_getspecific (pthread_key_t __key) __THROW;
参考资料
linux多线程pthread
http://www.oschina.net/question/234345_40357
linux man page
http://www.die.net/
TSD的例子
http://blog.csdn.net/lmh12506/article/details/8452700
简单的线程编程例子;
#include "multiThread.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
const int MAX = 10;
pthread_t thread[2];
pthread_mutex_t mut;
int number=0;
volatile int i;
void *thread1(void *)
{
printf ("thread1 : I'm thread 1\n");
for (i = 0; i < MAX; ++i)
{
printf("thread1 : number = %d: 1@%d\n", number, i);
pthread_mutex_lock(&mut);
++number;
pthread_mutex_unlock(&mut);
sleep(2);
}
printf("thread1 : are you waiting for me(1)\n");
pthread_exit(NULL);
}
void *thread2(void *)
{
printf("thread2 : I'm thread 2\n");
for (i = 0; i < MAX; ++i)
{
printf("thread2 : number = %d: 2@%d\n", number, i);
pthread_mutex_lock(&mut);
++number;
pthread_mutex_unlock(&mut);
sleep(2);
}
printf("thread2 : are you waiting for me(2)\n");
pthread_exit(NULL);
}
void thread_create(void)
{
int temp;
memset(&thread, 0, sizeof(thread)); //comment1
//创建线程
if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) //comment2
printf("thread1 create failed!\n");
else
printf("thread1 created!\n");
if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) //comment3
printf("thread2 create failed!\n");
else
printf("thread2 created!\n");
}
void thread_wait(void)
{
//等待线程结束
if(thread[0] !=0) //comment4
{
pthread_join(thread[0],NULL);
printf("thread1 run over!\n");
}
if(thread[1] !=0) //comment5
{
pthread_join(thread[1],NULL);
printf("thread2 run over!\n");
}
}
int multiTest()
{
//用默认属性初始化互斥锁
pthread_mutex_init(&mut,NULL);
printf("main(), thread CREATING\n");
thread_create();
printf("main(), thread WAITING\n");
thread_wait();
printf("number = %d", number);
return 0;
}
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
struct message
{
int i;
int j;
};
void *hello(void *ss)
{
struct message *str = (struct message *)ss;
printf("\nchild\n");
printf("pid = %d\n", getpid());
printf("tid = %lu, pid = %ld\n", pthread_self(), syscall(SYS_gettid));
printf("%d, %d\n", str->i, str->j);
//while(1);
getchar();
//pthread_exit();
return NULL;
}
/*
创建线程例子
*/
int PthreadCreate_Test()
{
struct message str;
str.i = 10;
str.j = 20;
pthread_t tid;
if(0 != pthread_create(&tid, NULL, hello, &str))
{
printf("error creating thread.");
abort();
}
printf("\nparent\n");
printf("pid = %d\n", getpid());
printf("tid = %lu, pid = %ld\n", pthread_self(), syscall(SYS_gettid));
if(0 != pthread_join(tid, NULL))
{
printf("error joining thread.");
abort();
}
return 0;
}
///
void *helloWorld(void *message)
{
char *str = (char *)message;
printf("\nmessage : %s\n", str);
printf("child tid = %lu\n", pthread_self());
int *p;
p = new int[10];
memset(p, 'c', 10);
p[1] = 10;
printf("%p\n", p);
pthread_exit(p);
}
/*
线程退出例子
*/
int PthreadExit_Test()
{
//int error;
int *temptr;
pthread_t thread_id;
if(0 != pthread_create(&thread_id, NULL, helloWorld, (void *)"HelloWorld"))
{
printf("error creating thread.");
abort();
}
printf("*p = %p, p = %p\n", *helloWorld, helloWorld);
if(0 != pthread_join(thread_id, (void **)&temptr))
{
printf("error joining thread.");
abort();
}
printf("temp = %p, *temp = %c, *temp = %d\n", temptr, *temptr, *(temptr+1));
*temptr = 'd';
printf("%c\n", *temptr);
delete[] temptr;
return 0;
}
///
void cleanup(void *)
{
printf("clean up.\n");
}
void * test_cancel(void *)
{
pthread_cleanup_push(cleanup, NULL);
printf("test_cancel\n");
while(1)
{
printf("test_message\n");
sleep(1);
}
pthread_cleanup_pop(1);
return NULL;
}
/*
线程退出前操作 例子
*/
int PthreadBeforeExit_Test()
{
pthread_t thread_id;
if(0 != pthread_create(&thread_id, NULL, test_cancel, NULL))
{
printf("error creating thread.");
abort();
}
sleep(2);
pthread_cancel(thread_id);
pthread_join(thread_id, NULL);
return 0;
}
///
static pthread_key_t key;
void echomsg(void *t)
{
printf("destructor excuted in thread %u, param = %p\n", pthread_self(), *((int *)t));
}
void *child1(void *)
{
int i = 10;
int tid = pthread_self();
printf("\nset key value %d in thread %u\n", i, tid);
pthread_setspecific(key, &i);
printf("child1 sleep 2 until child2 finish.\n");
sleep(2);
printf("\nthread %u returns %d, addr is %p\n", tid, *((int *)pthread_getspecific(key)),
(int *)pthread_getspecific(key));
}
void *child2(void *)
{
int i = 20;
int tid = pthread_self();
printf("\nset key value %d in thread %u\n", i, tid);
pthread_setspecific(key, &i);
sleep(1);
printf("\nthread %u returns %d, addr is %p\n", tid, *((int *)pthread_getspecific(key)),
(int *)pthread_getspecific(key));
}
/*
线程私有数据 例子
*/
int PthreadTSD_Test()
{
pthread_t tid1, tid2;
pthread_key_create(&key, echomsg);
if(0 != pthread_create(&tid1, NULL, child1, NULL))
{
printf("error creating thread child1.\n");
abort();
}
if(0 != pthread_create(&tid2, NULL, child2, NULL))
{
printf("error creating thread child1.\n");
abort();
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return 0;
}