您的位置:首页 > 产品设计 > UI/UE

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

2014-04-07 14:23 302 查看

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

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐