《APUE》chapter 12 Thread control 学习笔记(加上自己的代码)

Thread control


Thread Limits






Thread Attributes


the functions for managing these attributes follow the same pattern:

1.  Each object  is  associated  with  its  own  type  of  attribute  object  (threads  with  thread attributes, mutexes with mutex attributes, and so on). An attribute object  can represent multiple attributes. The attribute object is opaque to applications.  This  means  that  applications  aren’t  supposed  to  know  anything  about  its  internal  structure,  which  promotes  application  portability .Instead,  functions  are provided to manage the attributes objects.

2.  An initialization function exists to set the attributes to their default values.

3.  Another function  exists  to  destroy  the  attributes  object. If  the  initialization  function  allocated  any  resources  associated  with  the  attributes  object,  the  destroy function frees those resources.

4.  Each attribute has a function to get the value of the attribute from the attribute  object.  Because the function returns 0 on success or an error number on failure,  the value is returned to the caller by storing it in the memory location specified  by one of the arguments.

5.  Each attribute  has  a  function  to  set  the  value  of  the  attribute. In  this  case,  the  value is passed as an argument,by value.


#include <pthread.h>
int pthread_attr_init(pthread_attr_t * attr );
int pthread_attr_destroy(pthread_attr_t *attr );
Both return: 0 if OK, error number on failure

      If we know that we don’t need the thread’s termination status at the time we create the thread, we can arrange for the thread to start out in the detached state by modifying the detach state thread  attribute  in  the pthread_attr_t structure. 


#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr , int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t * attr ,int detachstate);
Both return: 0 if OK, error number on failure

我始终没搞明白不要这个termination status 直接丢弃就是了,干嘛还要专门搞个函数来处理。。。

重点还是理解一下pthread_attr_init  和pthread_attr_destroy吧。。。

#include <stdio.h>
#include <pthread.h>

void* thread_func(void*);

int main()
{
        int temp = 0;
        int err  = 0;
        int return_val = 0;

        pid_t           pid;
        pthread_t       tid;
        pthread_attr_t  attr;

        pid  = getpid();
        tid = pthread_self();
        printf("current pid :%u\ncurrent thread %u\n",(unsigned int)pid,(unsigned int)tid);

        err = pthread_attr_init(&attr);

        if(err != 0)
        {
                return err;
        }

        err = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

        if(err == 0)
        {
                err = pthread_create(&tid,NULL,thread_func,0);

                if(err != 0)
                {
                        printf("can't creat thread\n");
                }
        }

        err = pthread_join(tid,NULL);

        pthread_attr_destroy(&attr);

        return 0;
}

void* thread_func(void* smg)
{
        printf("thread processing\n");
        printf("current thread ID:%u\n",(unsigned int)pthread_self());
        printf("current PID:%u\n",(unsigned int)getpid());
        printf("thread end\n");
        return (void*)1;
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_12$ ./a.out
current pid :3625
current thread 540317504
thread processing
current thread ID:531998464
current PID:3625
thread end


#include <pthread.h>
int pthread_attr_getstack(const pthread_attr_t *restrict attr , void **restrictstackaddr ,size_t *restrict stacksize );
int pthread_attr_setstack(pthread_attr_t *attr ,void *stackaddr ,size_tstacksize );
Both return: 0 if OK, error number on failure

一般情况下我是不动stack的大小的。。。也不敢动。需求决定一切,以后有需求要改stack大小再demo。。。



Synchronization  Attributes


Mute xAttributes

#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t * attr );
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr );
Both return: 0 if OK, error number on failure



The process-shared mutex attribute is set to PTHREAD_PROCESS_PRIVATE .

If the process-shared mutex  attribute  is set  to PTHREAD_PROCESS_SHARED, a mutex  allocated  from  a memory  extent  shared between multiple processes may be used for synchronization by those processes


#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr , int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr , int pshared);
Both return: 0 if OK, error number on failure





APUE这种神作我不知道为什么,作者还是会出纰漏。。。斗胆挑一下大神写的代码段毛病,变量不初始化,没有意识到垃圾值给力指针,并尝试使用该指针。

这种现象我好像是第二次看到了,作者也不给个测试,只是给了代码。


void timeout(const struct timespec* when,void (*func)(void*),void *arg)
{
        struct timespec now;
        struct timeval  tv;
        struct to_info *tip;
        int             err;


        gettimeofday(&tv,NULL);


        now.tv_sec = tv.tv_sec;
        now.tv_nsec = tv.tv_usec*USECTONSEC;
        if((when->tv_sec > now.tv_sec) || (when->tv_sec == now.tv_sec && when->tv_nsec > now.tv_nsec))
        {
                if(tip != NULL)

上面就是个例子




Reader–Writer Lock Attributes


Reader–writer  locks  also  have  attributes,  similar  to  mutexes. We  use pthread_rwlockattr_init to  initialize  a pthread_rwlockattr_tstructure and pthread_rwlockattr_destroy to deinitialize the structure.

#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr );
int pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr );
Both return: 0 if OK, error number on failure




Reentrancy


If a function can be safely called by multiple threads at the same time, we say that the  function  is thread-safe .

If  a  function  is  reentrant  with  respect  to  multiple  threads,  we  say  that  it  is thread-safe.





Thread-Specific  Data



why would anyone want to promote interfaces that prevent sharing in this model? 


There are two reasons.

First, sometimes we need to maintain data on a per-thread basis.

The  second  reason  for  thread-private  data  is  to  provide  a  mechanism  for  adapting process-based interfaces to a multithreaded environment. 


Beforeallocating  thread-specific  data,  we  need  to  create  a key to  associate  with  the
data.  The key  will  be  used  to  gain  access  to  the  thread-specific  data. We  use
pthread_key_create to create such a key.


#include <pthread.h>
int pthread_key_create(pthread_key_t * keyp ,void (*destructor)(void *));
Returns: 0 if OK, error number on failure


If destructor is  null,  then  no  destructor  function  is  associated  with  the  key. When  the  thread  exits normally,either  by  calling pthread_exitor  by  returning,  the destructor  is  called. Also,  if  the  thread  is  canceled, the  destructor  is  called,  but  only after  the  last  cleanup  handler  returns.  But if  the  thread  calls exit, _exit, _Exit, or abort, or otherwise exits abnormally ,the destructor is not called.


We  can  break  the  association  of  a  key  with  the  thread-specific  data  values  for  all threads by calling pthread_key_delete .


#include <pthread.h>
int pthread_key_delete(pthread_key_t key);
Returns: 0 if OK, error number on failure



Note  that  calling pthread_key_delete will  not  invoke  the  destructor  function associated  with  the  key.To free any memory  associated  with  the  key’s  thread-specific data values, we need to take additional steps in the application.


pthread_key_creat 创建key的时候可能有不同进程竞争的现象


Depending  on  how  the  system  schedules  threads,  some  threads  might  see  one  key value, whereas other threads might see a different value. The way to solve this race to use pthread_once.

#include <pthread.h>
pthread_once_t initflag =PTHREAD_ONCE_INIT; 
int pthread_once(pthread_once_t * initflag ,void (*initfn )(void));
Returns: 0 if OK, error number on failure

The initflag must  be  a  nonlocal  variable  (i.e.,  global  or  static)  and  initialized  to PTHREAD_ONCE_INIT.



#include <pthread.h>
void *pthread_getspecific(pthread_key_t key);
Returns: thread-specific data value or NULL if no value has been associated with the key
int pthread_setspecific(pthread_key_t key,const void *value);
Returns: 0 if OK, error number on failure
If no thread-specific data has been associated with a key, pthread_getspecific will return a null pointer.




Cancel Options


        Two thread  attributes  that  are not  included  in  the pthread_attr_t structure are  t he cancelability  state and  the cancelability  type.These  attributes  affect  the  behavior  of  a thread in response to a call to pthread_cancel (Section 11.5). The cancelability  state attribute  can  be  either PTHREAD_CANCEL_ENABLE or
PTHREAD_CANCEL_DISABLE.A thread  can  change  its cancelability  state by  calling pthread_setcancelstate.


#include <pthread.h>
int pthread_setcancelstate(intstate ,int *oldstate);
Returns: 0 if OK, error number on failure





          When the state is set toPTHREAD_CANCEL_DISABLE,ac all to pthread_cancel will not  kill  the  thread.  Instead, the  cancellation  request  remains  pending  for  the  thread


         If your application doesn’t call one of the functions in Figure12.14 or Figur e12.15 for  a  long  period  of  time  (if  it  is  compute  bound,  for  example),  then  you  can  call pthread_testcancel to add your own cancellation points to the program.


#include <pthread.h>
void pthread_testcancel(void);


          When  you  call pthread_testcancel , if a cancellation  request  is  pending  and  if cancellation  has  not  been disabled,  the  thread  will  be  canceled.



Threads and  Signals


we discussed how processes can use the sigprocmask function to block signals from delivery.However,t he behavior of sigprocmask is undefined in a multithreaded process.  Threads have to use thepthread_sigmaskfunction instead.


#include <signal.h>
int pthread_sigmask(int how ,const sigset_t *restrict set, sigset_t *restrict oset );
Returns: 0 if OK, error number on failure





          The sigwaitfunction  will  atomically  unblock  the  signals  and  wait until one is delivered.  Before returning, sigwait will restore the thread’s signal mask



           The advantage to using sigwaitis that it can simplify signal handling by allowing us to treat asynchronously generated signals in a synchronous manner.


           To  send  a  signal  to  a  process,  we  call kill (Section  10.9). To  s end  a  signal  to   thread, we call pthread_kill.


#include <signal.h>
int pthread_kill(pthread_t thread ,int signo);
Returns: 0 if OK, error number on failure


             We  can pass a signo value of 0 to check for existence of the thread.  If the default action for a signal is to terminate the process, then sending the signal to a thread will still kill the entire process.




、、、、、、、、、、、、、、、、、、



Threads and fork


                By  inheriting  a  copy  of  the  address  space,  the  child  also  inherits  the  state  of  every mutex, reader–writer lock, and condition variable from the parent process.  If the parent consists of more than one thread, the child will need to clean up the lock state if it isn’t going to call exec immediately after fork re turns.

                Inside the child process, only one thread exists. It is made from a copy of the thread that called fork in the parent.  If the threads in the parent process hold any locks, the same locks will also be held in the child process.  The problem is that the child process doesn’t contain copies of the threads holding the locks, so there is no way for the child to know which locks are held and need to be unlocked.


To  clean  up  the  lock  state,  we  can  establish fork  handlers by  calling  the  function pthread_atfork.

#include <pthread.h>
int pthread_atfork(void (*prepare )(void), void (*parent )(void),
void (*child )(void));
Returns: 0 if OK, error number on failure




        With pthread_atfork, we can  install  up  to  three  functions  to  help  clean  up  the locks.  The prepare fork handler is  called  in  the  parent  before fork creates  the  child process.  This fork handler’s job is to acquire all locks defined by the parent.  The parent fork  handler  is  called  in  the  context  of  the  parent  after fork has  created  the  child process, but before fork has returned.  This fork handler’s job is to unlock all the locks acquired  by  the prepare fork handler .The child fork  handler  is  called  in  the  context  of the child process before returning from fork.Like the parent fork handler ,the child fork handler must release all the locks acquired by the prepare fork handler.



#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <myerr.h>

int quitflag;
sigset_t mask;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  wait = PTHREAD_COND_INITIALIZER;

void* thr_fn(void* arg)
{
        int err,signo;
    
        for(;;)
        {   
                err = sigwait(&mask,&signo);
                if(err != 0)
                {   
                        err_exit(err,"sigwait failed\n");
                }   
    
                switch(signo)
                {   
                        case SIGINT:
                        {   
                                printf("\ninterrupt\n");
                                break;
                        }   
    
                        case SIGQUIT:
                        {   
                                pthread_mutex_lock(&lock);
                                quitflag = 1;
                                pthread_mutex_unlock(&lock);
                                pthread_cond_signal(&wait);
                                return 0;
                        }   
                        default:
                        {
                                printf("exexpected signal %d\n",signo);
                                exit(1);
                        }
                }
        }
}

int main()
{
        int err;
        sigset_t        oldmask;
        pthread_t       tid;

        sigemptyset(&mask);
        sigaddset(&mask,SIGINT);
        sigaddset(&mask,SIGQUIT);

        if((err = pthread_sigmask(SIG_BLOCK,&mask,&oldmask)) != 0)
        {
                err_exit(err,"SIG_BLOCK error\n");
        }


        err = pthread_create(&tid,NULL,thr_fn,0);

        if(err != 0)
        {
                err_exit(err,"can't create thread\n");
                return 0;
        }

       err = pthread_create(&tid,NULL,thr_fn,0);

        if(err != 0)
        {
                err_exit(err,"can't create thread\n");
                return 0;
        }

        pthread_mutex_lock(&lock);
        while(quitflag == 0)
        {
                pthread_cond_wait(&wait,&lock);
        }

        pthread_mutex_unlock(&lock);

        quitflag = 0;

        if(sigprocmask(SIG_SETMASK,&oldmask,NULL) <0)
        {
                err_sys("SIG_SETMASK error\n");
        }

        exit(0);
}


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_12$ ./a.out^C
interrupt
^C
interrupt
^C
interrupt
^\









Threads and fork


     By  inheriting  a  copy  of  the  address  space,  the  child  also  inherits  the  state  of  every mutex, reader–writer lock, and condition variable from the parent process.  If the parent consists of more than one thread, the child will need to clean up the lock state if it isn’t going to call exec immediately after fork returns.


       If the threads in the parent process hold any locks, the same locks will also be held in the child process.  The problem is that the child process doesn’t contain copies of the threads holding the locks, so there is no way for the child to know which locks are held and need to be unlocked.这点在讲fork的时候讲过


#include <pthread.h>
int pthread_atfork(void (*prepare )(void), void (*parent )(void),
void (*child )(void));
Returns: 0 if OK, error number on failure



         With pthread_atfork, we can  install  up  to  three  functions  to  help  clean  up  the locks.  The prepare fork  handler is  called  in  the  parent  before fork creates  the  child process.  This fork handler’s job is to acquire all locks defined by the parent.  The parent fork  handler  is  called  in  the  context  of  the  parent  after fork has  created  the  child process, but before fork has returned.  This fork handler’s job is to unlock all the locks acquired  by  the prepare fork handler .The child fork  handler  is  called  in  the  context  of the child process before returning from fork.Like the parent fork handler ,the child fork handler must release all the locks acquired by the prepare fork handler。


demo:

#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <myerr.h>

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void prepare(void)
{
        printf("preparing locks ...\n");
        pthread_mutex_lock(&lock1);
        pthread_mutex_lock(&lock2);
}

void parent(void)
{
        printf("parent unlocking locks ...\n");
        pthread_mutex_unlock(&lock1);
        pthread_mutex_unlock(&lock2);
}

void child(void)
{
        printf("child unlocking...\n");
        pthread_mutex_unlock(&lock1);
        pthread_mutex_unlock(&lock2);
}

void* thr_fn(void* arg)
{
        printf("thread started ...\n");
        pause();//wait atfork to finish
        return (void*)1;
}

int main()
{
        int err;
        pid_t   pid;
        pthread_t       tid;

#if     defined(BSD) || defined(MACOS)

        printf("pthread_atfork is unsupported\n");
#else

        if((err = pthread_atfork(prepare,parent,child)) != 0)
        {
                err_exit(err,"can't install for handlers");
        }

        err = pthread_create(&tid,NULL,thr_fn,0);

        if(err != 0)
        {
                err_exit(err,"can't create thread\n");
        }

        sleep(2);
        printf("parent about to fork...\n");
        if((pid = fork()) < 0)
        {
                err_quit("fork failed\n");
        }
        else if(pid == 0)
        {
                printf("child returned from fork\n");
        }
        else
        {
                printf("parent returned from fork\n");
        }
#endif
        return 0;
}


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_12$ ./a.out
thread started ...
parent about to fork...
preparing locks ...
parent unlocking locks ...
parent returned from fork
child unlocking...
child returned from fork


最近几天效率极其低下。。。




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值