c/c++线程--1

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;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值