织梦CMS - 轻松建站从此开始!

罗索

线程模型

jackyhwei 发布于 2011-04-12 22:53 点击:次 
线程和函数调用很类似: 都与主程序(主线程)共享同样的存储空间;变量的使用范围也一样 --- 线程和函数都只能调用自己函数体内定义的变量和全局变量; 差别是线程的执行和调用
TAG:

来源:http://blog.csdn.net/mprc_jhq/archive/2007/03/07/1522972.aspx
1. 线程管理
线程和函数调用很类似:
都与主程序(主线程)共享同样的存储空间;变量的使用范围也一样---线程和函数都只能调用自己函数体内定义的变量和全局变量;
差别是线程的执行和调用线程的执行是并行(异步)的,而函数和调用函数的执行是串行的,所以要注意同步和互斥;
线程在传递参数和结果返回上有自己的接口。
 
线程函数,如果成功都返回0,如果不成功,都会返回非零的错误码。他们不设置errno,因此调用程序不能用perror来报告错误。必须用strerror (error)处理。
 
1.1 用ID引用线程
POSIX线程由Pthread_t类型的ID引用。
#include <pthread.h>
pthread_t pthread_self (void)  返回自己的线程ID
int pthread_equal (pthread_t t1, pthread_t t2)          比较线程ID是否相等,相等则返回非0值,不相等,返回0
1.2 线程创建
#include <pthread.h>
int pthread_create (pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *( *start_routine) (void *),void *restrict arg );
参数thread 指向新创建的线程ID。
参数attr 表示一个封装了线程的各种属性的属性对象,一般设置为NULL(默认属性)。
参数start_routine 是线程开始执行的时候调用的函数的名字。
函数start_routine 有一个由arg指定的参数,该参数是一个指向void的指针。
函数start_routine 返回一个指向void的指针,该返回值被pthread_join当作退出状态来处理。
参数arg 是传递给函数start_routine 的参数。
 
如果成功pthread_create 返回0,如果不成功,pthread_create返回一个非零的错误码 :
EAGAIN 系统没有创建线程所需的资源,或者创建线程会超出系统对一个进程中线程数的限制。
EINVAL attr参数是无效的
EPERM   调用程序没有适当的权限来设定调度策略或attr指定的参数
例:
#include <stdio.h>
#include <pthread.h>
 
void *start_routine (void *arg)
{
char *temp = (char *) arg ;
printf(“%s”, temp) ;
return NULL ;
}
 
int main(void)
{
int error ;
pthread   tid ;
char *buf = “hello” ;        //线程是在进程的地址空间运行的,所以可以传递指针。
if ( error = pthread_create(&tid, NULL, start_routine, (void *)buf ) )
{
fprintf (stderr, “Failed to create pthread: %s\n”, strerror (error)) ;
exit(1);
}
 
exit(0);
}
 
1.3    分离和连接
在 Linux 中,缺省情况下线程是以一种可连接(joinable)状态被创建的。在可连接状态下,其他线程可以在一个线程终止时对其进行同步,然后用 pthread_join()函数恢复其终止代码。这个可连接线程的线程资源只有在它被插入之后才能释放。
在分离(detached)状态下,线程的资源在线程终止时会被立即释放。您可以通过对线程属性对象调用 pthread_attr_setdetachstate()来设置分离状态:
 
分离#include <pthread.h>
int pthread_detach (pthread_t thread); 
pthread_detach函数成功返回0,不成功,返回一个非零的错误码如下:
EINVAL thread 对应的不是一个可接合的线程
ESRCH   没有ID为thread的线程
例1:
void *thr_fun(void *arg);
{}
 
int main(void)
{
int        error;
pthread_t   tid;
if(error = pthread_create(&tid, NULL, thr_fun, NULL);
{
fprintf(stderr, “Failed to create thread: %s\n”, strerror(error) );
exit(1);
}
if(error = pthread_detach(tid) )       //主线程执行分离
{
Fprintf(stderr, “Failed to detach thread: %s\n”, strerror(error);
Exit(2);
}
 
Exit(0);
}
 
例2:
void *thr_fun(void *arg)
{
int error;
if( error = pthread_detach(pthread_self()) )   //线程函数,自己分离出去
{
fprintf( stderr, “Failed to detach :%s\n”, strerror(error) );
exit(1);
}
return NULL;
}
 
连接#include <pthread.h>
int pthread_join (pthread_t thread ,void **value_ptr);
pthread_join函数成功返回0,不成功,返回一个非零的错误码如下:
EINVAL thread 对应的不是一个可接合的线程
ESRCH   没有ID为thread的线程
pthread_join 函数调用函数挂起,直到第一个参数指定的目标线程终止为止。
参数value_ptr 为指向返回值的指针,这个返回值由pthread_exit或者return 返回
 
1.4    退出和取消
1.4.1退出
#include <pthread.h>
void pthread_exit( void *value_ptr);
POSIX没有为pthread_exit 定义任何错误。
value_ptr 必须指向线程退出后仍然存在的数据,因此线程不应该为value_ptr使用指向自动局部数据的指针(分配在栈上)。代之以malloc函数分配结构或全局结构。。
例:
#include <pthread.h>
#include <string.h>
#include <stdio.h>
 
 
struct foo
{
    int a;
    int b;
    int c;
    int d;
};
void printfoo(const char *s, const struct foo *fp)
{
    printf(s);
    printf(" structure at 0x%x\n", (unsigned)fp);
    printf(" foo.a = %d\n", fp->a);
    printf(" foo.b = %d\n", (*fp).b);
    printf(" foo.c = %d\n", fp->c);
    printf(" foo.d = %d\n", fp->d);
}
void * thr_fn1(void *arg)
{
    struct foo foo = {1,2,3,4};
 
    printfoo("thread 1:\n", &foo);
    pthread_exit( (void *)&foo );    //   返回变量的指针
}
 
void * thr_fn2(void *arg)
{
    printf("thread 2: ID is %d\n", pthread_self());
    pthread_exit( (void *)0 );
 
}
 
 
int main(void)
{
    int        err;
    pthread_t tid1;
    pthread_t tid2;
    struct foo *fp;               //定义相应变量的指针准备接收
 
    if( err = pthread_create(&tid1, NULL, thr_fn1, NULL) )
    {
        fprintf( stderr, "Failed to create thread 1:%s\n", strerror(err) );
        exit(1);
    }
    if( err = pthread_join(tid1, (void **)&fp ) ) //传递指向指针的指针给函数,获得返回值
    {
        fprintf( stderr, "Failed to join thread 1:%s\n", strerror(err) );
        exit(2);
    }
 
    sleep(1);
 
    printf("parent starting second thread\n");
 
    if( err = pthread_create(&tid2, NULL, thr_fn2, NULL) )
    {
        fprintf( stderr, "Failed to create thread 2:%s\n", strerror(err) );
        exit(3);
    }
 
    sleep(1);
 
    printfoo("parent:\n", fp);
 
}
结果:(由于struct foo foo分配在栈上,被第二个线程覆盖,出现了问题)
thread 1:
 structure at 0xb7e3c430
 foo.a = 1
 foo.b = 2
 foo.c = 3
 foo.d = 4
parent starting second thread
thread 2: ID is -1209807952
parent:
 structure at 0xb7e3c430
 foo.a = -1208727122
 foo.b = -1208557808
 foo.c = -1208598540
 foo.d = -1208605084
 
 
1.4.2取消
#include <pthread.h>
int pthread_cancel (pthread_t thread);
如果成功,函数返回0;不成功,返回非零的错误码,没有定义必须检测的错误;
pthread_cancel 有一个参数,这个参数是要取消的目标线程的ID。
pthread_cancel 不阻塞调用线程,发出取消请求后就返回了,结果有目标线程的类型和取消状态决定。
线程处于PTHREAD_CANCEL_ENABLE状态,它就接受取消请求(默认情况)。
线程处于PTHREAD_CANCEL_DISABLE状态,取消请求将被挂起。
 
pthread_setcancelstate 函数可以改变调用线程的取消状态
#include <pthread.h>
int pthread_setcancelstate( int state, int *oldstate );
如果成功,返回0;不成功,返回一个非零的错误码,没有定义必须检测的错误
state 说明要设置的新状态;oldstate 指向一个整数的指针,这个整数中装载了以前的状态。
作为一个通用的原则,改变了其取消状态或类型的函数应该在返回之前恢复他们的值。
 
pthread_setcanceltype函数可以改变调用线程的取消类型
#include <pthread.h>
int pthread_setcanceltype( int type, int *oldtype);
void pthread_testcancel(void);
pthread_setcanceltype函数如果成功,返回0;如果不成功就返回一个错误码,没有定义必须检测错误码
当线程取消状态是DISABLE时,有取消请求则会挂起该请求(称为未决请求);当线程的取消状态变为ENALBE时,线程将在下一个取消点上对所有的未决的取消请求进行处理:
线程处于PTHREAD_CANCEL_ASYNCHRONOUS(异步取消), 则立即取消线程;
线程处于PTHREAD_CANCEL_DEFERRED(延迟取消), 则到下一个取消点才取消线程;
pthread_testcancel函数用来设置取消点,POSIX还定义了很多一些其他取消点(详见《UNIX环境高级编程》P332 表12-7
 
1.5向线程传递参数并将值返回
1.5.1 线程内动态分配空间
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
 
#define BLKSIZE     1024
#define WRITE_FLAG (O_WRONLY|O_CREAT|O_TRUNC)
#define READ_FLAG   O_RDONLY
#define PERM        (S_IRUSR|S_IWUSR)
 
int copyfd (fromfd, tofd)
{
    char    buf[BLKSIZE];
    int     readbytes;
    int     writebytes;
    int     totalbytes;
 
    totalbytes = 0;
 
    for ( ; ;)
    {
        if ( (readbytes = read(fromfd, buf, BLKSIZE)) <= 0 )
        {
            break;
        }
        if ( (writebytes = write(tofd, buf, readbytes)) == -1 )
        {
            break;
        }
 
        totalbytes += writebytes;
 
    }
 
    return totalbytes;
 
}
 
void * copyfile (void *arg)
{
    int       *i;
    int       fromfd;
    int       tofd;
    int       error;   
    fromfd = *((int *)arg);
    tofd   = *((int *)arg + 1);
    i = malloc (sizeof(int));
    (*)i = copyfd (fromfd, tofd); 
    return (void *)i; 
}
int main (int argc, char *argv[])
{
    int *          n;    //同线程函数返回值一个类型
    int            error;
    pthread_t      tid;
    int            fd[2];
 
    if (argc != 3)
    {
        fprintf (stderr, "Usage: %s fromfile tofile\n", argv[0]);
        exit(1);
    }
 
    if ( (fd[0] = open (argv[1], READ_FLAG)) == -1 )
    {
        perror ("Failed to open fromfile");
        exit(2);
    }
    if ( (fd[1] = open (argv[2], WRITE_FLAG, PERM)) == -1)
    {
        perror ("Failed to open tofile");
        exit(3);
    }
 
    if ( (error = pthread_create (&tid, NULL, copyfile, (void *)fd)) != 0 )
    {
        fprintf (stderr, "Failed to create thread:%s\n", strerror(error));
        exit(4);
    }
 
    if ( (error = pthread_join (tid, (void **)&n)) != 0 )
    {
        fprintf (stderr, "Failed to join thread:%s\n", strerror(error));
        exit(5);
    }
 
    printf ("copy bytes:%d\n", (*n));
 
    exit(0);
 
}
程序绿色部分是线程向调用函数返回值:返回值一定是指针;返回值所指必须是malloc申请的堆上数据或者是全局变量,不可用自动的或静态的变量(栈上数据在函数完成后将被释放);
与普通函数调用返回值得关系:如果返回值是指针,所指必须是malloc申请的堆上数据或者是全局变量,不可用自动的或静态的变量(栈上数据在函数完成后将被释放);返回值还可以不是指针,则可以返回栈上数据(值拷贝性质的);
程序红色部分是主程序向线程传递参数
1.5.2 由于动态分配空间来装载单个整数效率很低;所以,另一种方法是由调用线程分配线程返回值所指空间
例:
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
 
#define   READ_FLAG    O_RDONLY
#define   WRITE_FLAG   (O_WRONLY|O_CREAT|O_TRUNC)
#define   PERM         (S_IRUSR|S_IWUSR)
#define   BLKSIZE      1024
 
int copyfd (int fromfd, int tofd)
{
    int       readbytes;
    int       writebytes;
    int       totalbytes;
    char      buf[BLKSIZE];
 
    totalbytes = 0;
 
    for (; ;)
    {
        if ( (readbytes = read (fromfd, buf, BLKSIZE)) <= 0 )
        {
            break;
        }
        if ( (writebytes = write (tofd, buf, readbytes)) == -1 )
        {
            break;
        }
 
        totalbytes += writebytes;
 
    }
 
    return totalbytes;
 
}
 
void * copyfilepass (void *arg)
{
    int      *argint;
 
    argint = (int *)arg;
 
    argint[2] = copyfd (argint[0], argint[1]);
 
    close (argint[0]);      //一定要关闭打开的文件描述符
    close (argint[1]);
 
 return (void *)(argint+2); //通过pthread_exit返回指针,也可以通过targs[2]直接访问
(但是不推荐)。
 
}
 
(mprc_jhq/)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201104/11190.html]
本文出处:CSDN博客 作者:mprc_jhq/
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容