linux + thread + c (一)

linux + thread + c (一)
/*
    进程与线程之间,线程优势:
    其一:线程是一种耗资源非常节约的多任务操作方式,在linux下创建一个进程必须给它
          分配独立的的地址空间,建立众多的数据表来维护它的代码段,堆栈段和数据段,
          这是一种耗资源很奢侈的多任务操作方式。在运行一个进程下,创建多个线程,他
          们彼此之间共用相同的地址空间,而且线程之间切换比进程之间切换多需要的时间
          小得多。
    其二:线程之间通信有着方便的机制,而进程它们具有独立的数据空间,要进行数据的传
          递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由
          于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程
          所用,这不仅快捷,而且方便。
*/
#include <stdio.h>
#include <pthread.h>
#include <time.h>
pthread_mutex_t mut;
int pubRecord;
void *thread1(void)
{
    int i;
    for (i =0; i <10; i++)
    {
        pthread_mutex_lock(&mut);
        pubRecord++;
        printf("my name is subthread1-thread = %d\n",pubRecord);
        pthread_mutex_unlock(&mut);
        sleep(1);
    }
}
void *thread2(void)
{
    int i;
    for (i =0 ; i<10; i++)
    {
        pthread_mutex_lock(&mut);
        pubRecord++;
        printf("my name is subthread2-thread = %d\n",pubRecord);
        pthread_mutex_unlock(&mut);
        sleep(1);
    }
}
int main(int argc, char *argv[])
{
    pthread_t ID1,ID2;
    int ret,i;
    pubRecord = 0;
    ret = pthread_create(&ID2, NULL, (void *)thread2, NULL);
    if (ret !=0 )
    {
        printf("create sub-thread2 error!\n");
        exit(1);
    }
    ret = pthread_create(&ID1, NULL, (void *)thread1, NULL);
    if (ret !=0 )
    {
        printf("create sub-thread1 error!\n");
        exit(1);
    }
    for (i =0; i < 10; i++)
    {
        printf("my name is main-thread\n");
        sleep(1);
    }
    pthread_join(ID1,NULL);
    pthread_join(ID2,NULL);
    return 0;
}

/* 说       明:pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
                typerdef unsigned long int pthread_t它是一个线程的标识符
       
        函数pthread_create用来创建线程,其原型
    extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
  void *(*__start_routine) (void *), void *__arg));
    第一个参数:指向线程标志符的指针
    第二个参数:用来设置线程的属性
    第三个参数:线程运行函数的起始地址
    第四个参数:运行函数的参数
       
        函数pthread_join用来等待一个线程的结束
    第一个参数:被等待线程的标识符
    第二个参数:用户定义的指针,可以用来存储被等待线程的返回值
    作      用:这是一个阻塞函数,调用它的函数,一直等待被等待函数结束返回,当函数返回后,
                     被等待线程的资源被收回。
       
        函数pthread_mutex_init用来生成一个互斥锁。
    第一个参数:声明互斥变量
    第二个参数:声明特定属性的互斥锁,NULL是默认属性
    说      明:声明特定属性互斥锁,须调用pthread_mutexattr_init.函数pthread_mutexattr_set
                pthread和函数pthread_mutexattr_settpye用来设置互斥锁属性,前一个函数设置pshared
                属性,它又二个取值PTHREAD_PROCESS_PRIVATE,PTHREAD_PROCESS_SHARED,前者用来不同进程间同步,后者用于同一进程,不同线程同步。默认是PTHREAD_PROCESS_ PRIVATE后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK    'PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。
   
        函数pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至pthread_mutex_unlock为止,均被上锁。如果该锁此时正被另一个线程使用,那此线程阻塞。
*/

linux +thread +c(二 )

修改线程的属性:
           1.属性结构为pthread_attr_t,它在头文件/usr/include/pthread.h中定义。属性值不能直接设置
,需要使用相关的函数进行操作,初始化函数为pthread_attr_init,这个函数必须在pthread_create
函数前调用。属性对象主要包括是否绑定,是否分离,堆栈地址,堆栈大小,优先级。默认属性非绑
定,非分离,缺省1M的堆栈,与父进程同样级别的优先级。

         <1>线程绑定。需描述另一个概念:轻进程(LWP:Light Weight Process),轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配,对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认情况下,启动多少轻进程,那些轻进程来控制那些线程是有系统来控制的,这种情况为非绑定。绑定情况,即某个线程固定的“绑”在一个轻进程之上,被绑定的线程具有较高的响应速度,只是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻
进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足实时反应之类的要求。设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定)PTHREAD_SCOPE_PROCESS(非绑定)。
代码如下:
#include <pthread.h>
pthread_attr_t attr;
pthread_t id;
pthread_attr_init(&attr);
pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);
pthread_create(&id,&attr,(void) thread_function,NULL);
         <2>线程分离.其决定一个线程以什么样的方式来终止自己。非分离状态,原有的线程等待创建的线程结束,只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占有的系统资源。分离线程:它没有被其他的线程所等待,自己运行结束了,线程也就终止了,并马上释放资源,设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr *attr,int detachstate),第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD_CREATE_JOINABLE(非分离线程)。注意:如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthead_create函数返回之前就终止,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使 整个 进程睡眠,并不能解决线程同步的问题。
            <3>线程优先级。它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
代码如下:
#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int newprio=20;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, PTHREAD_CREATE_JOINABLE);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&tid, &attr, (void *)myfunction, NULL);

linux + c + thread(三)

1.线程数据:在单线程的程序里,又两种基本数据(全局变量,局部变量)。但在多线程程序里   ,还有第三种线程类型(线程数据 TSD: Thread-Specific Data).它和全局变量很像,在线程内部,每个函数都可以调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见,如errno,它返回标准的出错信息,它显然不能是一个局部变量,几乎每个函数都可以调用它,它又不能是一个全局变量,否则在A线程里输出的很可能是B线程的出错信息。要实现此类变量,就必须使用线程数据。实现的方式,为每个线程创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同,在同一个线程里,它代表同样的数据内容。
相关函数:
(1)创建一个键
   extern int pthread_key_create_p((pthread_key_t *_key,void(*_destr_function) );
第一个参数:指向一个键值的指针
第二个参数:指明一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once((pthread_once_t *once_control,void (*initoutine) (void)))一起使用。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将它忽略。
   例子:定义一个函数createWindow,这个函数定义一个图形窗口(数据类型F1_Window *,这个图形界面开发工具FLTK中的数据类型).由于各个线程都会调用这个函数,所以我们使用线程数据。
pthread_key_t myWinKey;                           //声明一个键
void createWindow(void)
{
   F1_Window * win;
   static pthread_once_t once = PTHREAD_ONCE_INIT;
   pthread_once(&once,createMyKey);         //调用createMyKey,创建键
   win = new F1_Window(0,0,100,"MyWindow");//指向一个新建立的窗口
   setWindow(win);                                          //对窗口进行设置(大小,位置等)
   pthread_setspecific(myWinKey,win);      //将窗口指针绑定在键myWinKey上;
}

void createMyKey(void)                                   //创建一个键,并指定destructor
{
   pthread_keycreate(&myWinKey,freeWinKey);
}

void freeWinKey(F1_Window * win)               //释放键空间
{
   delete win;
}
   这样,在不同的线程中调用函数createWindow,都可以得到在线程内部均可见的窗口变量,这个变量通过函数pthread_getspecific得到。在上面的例子中,我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数原型如下:
    extern int pthread_setspecific _P ((pthread_key_t _key, _const void * _pointer));
    extern void * pthread_getspecific _P((pthread_key_t _key));
   注意:用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的时,它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不回触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。

2.互斥锁
         pthread_mutex_init用来生成一个互斥锁,NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init.函数pthread_mutexattr_setshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值,PTHREAD_PROCESS_PRIVAGE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步奔进程的不同线程,默认属性PTHREAD_PROCESS_PRIVATE.后者用来设置互斥类型,可选的类型PTHREAD_MUTEX_NORMAL,PTHREAD_MUTEX_ERRORCHECK,PTHREAD_MUTEX_RECURSIVE和PTHREAD_MUTEX_DEFAULT.它们分别定义了不同的上锁,解锁机制,一般情况下,选用最后一个默认属性。
       互斥锁的过程中可能回出现死锁,可以使用函数pthread_mutex_trylock,它时函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。这一点让我想到了操作系统上的PV操作

Linux + c + thread(四)

1.条件变量
互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态(锁定和非锁定);而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化,一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程,这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化条件变量,它的原型为:
extern int pthread_cond_init _p ((pthread_cond_t *cond,_const pthread_condattr_t *_cond_attr));
其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或释放。释放一个条件变量的函数未pthread_cond_destroy(pthread_cond_t cond).
函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:
extern int pthread_cond_wait _P((phtread_cond_t * _cond,phtread_mutex_t * _mutex));
线程解开mutex指向的锁并被条件变量cond阻塞,线程可以被函数pthread_cond_signal和函数
pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是阻塞和唤醒线程的作用,具体的判断条件还需要用户给出,例如一个变量是否为0等等,如果还不满足,一般说来线程因该仍然阻塞在这里,被等待被下一个唤醒。这个过程一般用while语句实现。
另一个用来阻塞线程的函数是pthread_cond_timedwait(),它的原型为:
extern int pthread_cond_timedwait _P((pthread_cond_t * _cond,pthread_mutex_t * _mutex,_const struct timespec * _abstime));它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。
函数pthread_cond_signal()的原型为:
extern int pthread_cond_signal _P((pthread_cond_t * _cond));
它用来释放被阻塞在条件变量cond上的一个线程,多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。
例子如下:
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count()
{
   while(count == 0)
           pthread_cond_wait(&count_nonzero,&count_lock);
   count = count - 1;
    pthread_mutex_unlock(&count_lock);
}

increment_count()
{
    pthread_mutex_lock(&count_lock);
    if(count == 0)
        pthread_cond_signal(&count_nonzero);
    count = count + 1;
     pthread_mutex_unlock(&count_lock);
}
count值为0时,decrement函数在pthread_cond_wait处被阻塞,并打开互斥锁count_lock.此时,当调用到函数increment_count时,pthread_cond_signal()函数改变条件变量,告知decrement_count()停止阻塞。
函数pthread_cond_broadcase(pthread_cond_t * cond) 用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值