C 多线程编程以及线程函数

C 多线程编程以及线程函数

优势

  • 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式

  • 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。

  • 线程则不然,由于同一进程下的线程之间共享数据空间,】

  • 多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

  • 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
  • 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
  • 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改

多线程创建

extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr, void *(*__start_routine) (void *), void *__arg));

  • 第二个参数是线程属性 (NULL为默认),
  • 第三个是线程的函数 , 第三个是函数的参数
  • 创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

  • 函数pthread_join用来等待一个线程的结束。函数原型为:
    extern int pthread_join __P ((pthread_t __th, void **__thread_return));

  • 第二个参数为用户自定义指针, 储存返回值
  • 此函数为线程阻塞函数 , ,调用它的函数将一直等待到被等待的线程结束为止。

demo

#include <stdio.h>
#include <pthread.h>
 
void thread(void)
{
  int i;
  for(i=0;i<3;i++)
    printf("This is a pthread.\n");
}
 
 int main(void)
{
  pthread_t id;        /unsigned long int
  int i,ret;
  ret=pthread_create(&id,NULL,(void *) thread,NULL); // 成功返回0,错误返回错误编号
  if(ret!=0) {
    printf ("Create pthread error!\n");
    exit (1);
  }
  for(i=0;i<3;i++)
    printf("This is the main process.\n");
  pthread_join(id,NULL);
  return (0);
}
    我们编译此程序: 
    gcc example1.c -lpthread -o example1 
    运行example1,我们得到如下结果: 
    This is the main process. 
    This is a pthread. 
    This is the main process. 
    This is the main process. 
    This is a pthread. 
    This is a pthread. 
    再次运行,我们可能得到如下结果: 
    This is a pthread. 
    This is the main process. 
    This is a pthread. 
    This is the main process. 
    This is a pthread. 
    This is the main process. 

线程的属性pthread_attr_t

线程的属性pthread_attr_t 不能直接设置
用函数初始化的函数为pthread_attr_init()

属性对象主要包括:

  • 是否绑定、是否分离、堆栈地址、堆栈大小、优先级
  • 默认的属性为非绑定、非分离、缺省1M的堆栈、
  • 与父进程同样级别的优先级。

设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)

线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

设置线程分离函数 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)
  • 如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,

  • 它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号:

  • 解决:
    1 :在创建的线程里调用pthread_cond_timewait()函数(设置等待) ,流出足够的时间让pthread_create()返回,
    但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。


* 另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中 * 用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去 ``` #include


demo

#include <pthread.h>
pthread_attr_t attr; pthread_t tid; /*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid, &attr, (void *) my_function, NULL);```


## 数据线程
线程的数据处理和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义,这是为了防止编译器在优化时(如gcc中使用-OX参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。 

* 在单线程中有两种基本数据:全局和变量。 
* 在多线程中还包括:数据线程
 * 在函数内部各个函数像用全局变量一样的调用,在线程外是不可见的
 * 要使用数据线程必须先创建一个键与之相关联,在各个线程里使用键代表数据线程,,不同的线程里键代表的数据不同。

---


 键函数:
		 extern int pthread_key_create __P ((pthread_key_t *__key, void (*__destr_function) (void *)));
 
> 第一个参数指向键的指针, 
> 第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块
> >  这个函数常和函数`pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))`一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。 

如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值

* 步骤
 *  创建一个键
 *  为一个键设置线程私有数据
 *  从一个键读取线程私有数据void*pthread_getspecific(pthread_key_t key);
 *  线程退出(退出时,会调用destructor释放分配的缓存,参数是key所关联的数据)
 * 删除一个键 

`int pthread_setspecific(pthread_key_t key,const void *pointer));`
`void *pthread_getspecific(pthread_key_t key);`

* set是把一个变量的地址告诉key,一般放在变量定义之后,get会把这个地址读出来,然后你自己转义成相应的类型再去操作,注意变量的有效期
* 在不同的线程里可以操作同一个key,他们不会冲突,比如线程a,b,c set同样的key,分别get得到的地址会是之前各自传进去的值。
* 这样做的意义在于,可以写一份线程代码,通过key的方式多线程操作不同的数据

/*三个线程:主线程,th1,th2各自有自己的私有数据区域
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

static pthread_key_t str_key;
//define a static variable that only be allocated once
static pthread_once_t str_alloc_key_once=PTHREAD_ONCE_INIT;
static void str_alloc_key();
static void str_alloc_destroy_accu(void* accu);

char* str_accumulate(const char* s)
{ char* accu;

pthread_once(&str_alloc_key_once,str_alloc_key);//str_alloc_key()这个函数只调用一次
accu=(char*)pthread_getspecific(str_key);//取得该线程对应的关键字所关联的私有数据空间首址
if(accu==NULL)//每个新刚创建的线程这个值一定是NULL(没有指向任何已分配的数据空间)
{    accu=malloc(1024);//用上面取得的值指向新分配的空间
    if(accu==NULL)    return NULL;
    accu[0]=0;//为后面strcat()作准备
  
    pthread_setspecific(str_key,(void*)accu);//设置该线程对应的关键字关联的私有数据空间
    printf("Thread %lx: allocating buffer at %p\n",pthread_self(),accu);
 }
 strcat(accu,s);
 return accu;

}
//设置私有数据空间的释放内存函数
static void str_alloc_key()
{ pthread_key_create(&str_key,str_alloc_destroy_accu);/创建关键字及其对应的内存释放函数,当进程创建关键字后,这个关键字是NULL。之后每创建一个线程os都会分给一个对应的关键字,关键字关联线程私有数据空间首址,初始化时是NULL/
printf(“Thread %lx: allocated key %d\n”,pthread_self(),str_key);
}
/线程退出时释放私有数据空间,注意主线程必须调用pthread_exit()(调用exit()不行)才能执行该函数释放accu指向的空间/
static void str_alloc_destroy_accu(void* accu)
{ printf(“Thread %lx: freeing buffer at %p\n”,pthread_self(),accu);
free(accu);
}
//线程入口函数
void* process(void arg)
{ char
res;
res=str_accumulate(“Resule of “);
if(strcmp((char*)arg,“first”)==0)
sleep(3);
res=str_accumulate((char*)arg);
res=str_accumulate(” thread”);
printf(“Thread %lx: “%s”\n”,pthread_self(),res);
return NULL;
}
//主线程函数
int main(int argc,char* argv[])
{ char* res;
pthread_t th1,th2;
res=str_accumulate("Result of ");
pthread_create(&th1,NULL,process,(void*)“first”);
pthread_create(&th2,NULL,process,(void*)“second”);
res=str_accumulate(“initial thread”);
printf(“Thread %lx: “%s”\n”,pthread_self(),res);
pthread_join(th1,NULL);
pthread_join(th2,NULL);
pthread_exit(0);
}
复制代码


### demo 我们要定义一个函数createWindow

pthread_key_t myWinKey;

/* 函数 createWindow /
void createWindow ( void )
{
  Fl_Window * win;
  static pthread_once_t once= PTHREAD_ONCE_INIT;
  /
调用函数createMyKey,创建键*/
  pthread_once ( &once, createMyKey) ;
  /win指向一个新建立的窗口/
  win=new Fl_Window( 0, 0, 100, 100, “MyWindow”);
  /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/
  setWindow(win);
  /* 将窗口指针值绑定在键myWinKey上*/
  pthread_setpecific ( myWinKey, win);
}

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

/* 函数 freeWinKey,释放空间*/
void freeWinKey ( Fl_Window * win)
{
  delete win;
}```

互斥锁

  • 用来保证一段时间内只用一个线程在执行代码

  • 互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。

  • pthread_mutex_t mutex;

  • pthread_mutex_init (&mutex,NULL);

  • 函数pthread_mutex_init用来生成一个互斥锁

  • NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init();

  • 函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性

  • pthread_mutexattr_setpshared和函数设置属性pshared,它有两个取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步

  • pthread_mutexattr_settype函数用来不同进程中的线程同步,后者用于同步本进程的不同线程可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。

  • pthread_mutex_lock (&mutex); 锁定一下的代码

  • pthread_mutex_unlock (&mutex); 解锁上的代码

void reader_function ( void );
void writer_function ( void );
char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
struct timespec delay;
void main ( void )
{
  pthread_t reader;
  /* 定义延迟时间*/
  delay.tv_sec = 2;
  delay.tv_nec = 0;
  /* 用默认属性初始化一个互斥锁对象*/
  pthread_mutex_init (&mutex,NULL);
  pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);
  writer_function( );
}
 
void writer_function (void)
{
  while(1) {
    /* 锁定互斥锁*/
    pthread_mutex_lock (&mutex);
    if (buffer_has_item==0) {
      buffer=make_new_item( );
      buffer_has_item=1;
    }
    /* 打开互斥锁*/
    pthread_mutex_unlock(&mutex);
    pthread_delay_np(&delay);
  }
}
 
void reader_function(void)
{
  while(1) {
    pthread_mutex_lock(&mutex);
    if(buffer_has_item==1)
    {
      consume_item(buffer);
      buffer_has_item=0;
     }
    pthread_mutex_unlock(&mutex);
    pthread_delay_np(&delay);
  }
}```
<br>

thread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。在上面的例子中,我们使用了pthread_delay_np函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。(如果用sleep(2)则是让进程睡眠了)
<br>
---
## 读写锁

* 读写锁特点:
1)多个读者可以同时进行读
2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

pthread_rwlock_init()
pthread_rwlock_rdlock()
pthread_rwlock_wrlock()
pthread_rwlock_unlock()


* 读写锁的属性设置

/* 初始化读写锁属性对象 */
int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr);

/* 销毁读写锁属性对象 */
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr);

/* 获取读写锁属性对象在进程间共享与否的标识*/
int pthread_rwlockattr_getpshared (__const pthread_rwlockattr_t * __restrict __attr,
int *__restrict __pshared);

/* 设置读写锁属性对象,标识在进程间共享与否 */
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr, int __pshared);

                                                返回值:成功返回0,否则返回错误代码```
  • 读写锁的使用
/* 读模式下加锁  */
int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock);
 
/* 非阻塞的读模式下加锁  */
int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock);
 
# ifdef __USE_XOPEN2K
/*  限时等待的读模式加锁 */
int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock,
                                       __const struct timespec *__restrict __abstime);
# endif
 
/* 写模式下加锁  */
int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock);
 
/* 非阻塞的写模式下加锁 */
int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock);
 
# ifdef __USE_XOPEN2K
/* 限时等待的写模式加锁 */
int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock,
                                       __const struct timespec *__restrict __abstime);
# endif
 
/* 解锁 */
int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock);
 
                                                   返回值:成功返回0,否则返回错误代码

条件变量

条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用

条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:
extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));

  • cond_attr是一个指向结构pthread_condattr_t的指针
  • cond是一个指向结构pthread_cond_t的指针
  • 默认值是PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用
  • 注意初始化条件变量只有未被使用时才能重新初始化或被释放。
  • 释放一个条件变量的函数为pthread_cond_ destroy(pthread_cond_t cond)。

函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:
extern int pthread_cond_wait __P((pthread_cond_t *__cond, pthread_mutex_t *__mutex));

线程解开mutex指向的锁并被条件变量cond阻塞
线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒,

条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等

  • 另一个用来阻塞线程的函数是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上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。

demo

pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count()
{
  pthread_mutex_lock (&count_lock);
  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);
}   ```
函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。
---
## 信号量
4.4 信号量

  信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数,它们都在头文件/usr/include/semaphore.h中定义。

  信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为: 

  extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));

  sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。

  函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。 
  函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。 
  函数sem_destroy(sem_t *sem)用来释放信号量sem。

  下面我们来看一个使用信号量的例子。在这个例子中,一共有4个线程,其中两个线程负责从文件读取数据到公共的缓冲区,另两个线程从缓冲区读取数据作不同的处理(加和乘运算)。

/* File sem.c /
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size=0;
sem_t sem;
/
从文件1.dat读取数据,每读一次,信号量加一*/
void ReadData1(void)
{
  FILE *fp=fopen(“1.dat”,“r”);
  while(!feof(fp)) {
    fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
    sem_post(&sem);
    ++size;
  }
  fclose(fp);
}
/从文件2.dat读取数据/
void ReadData2(void)
{
  FILE fp=fopen(“2.dat”,“r”);
  while(!feof(fp)) {
    fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
    sem_post(&sem);
    ++size;
  }
  fclose(fp);
}
/阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待/
void HandleData1(void)
{
  while(1) {
    sem_wait(&sem);
    printf(“Plus:%d+%d=%d\n”,stack[size][0],stack[size][1], stack[size][0]+stack[size][1]);
    --size;
  }
}
void HandleData2(void)
{
  while(1) {
    sem_wait(&sem);
    printf("Multiply:%d
%d=%d\n",stack[size][0],stack[size][1], stack[size][0]*stack[size][1]);
    --size;
  }
}
int main(void)
{
  pthread_t t1,t2,t3,t4;
  sem_init(&sem,0,0);
  pthread_create(&t1,NULL,(void *)HandleData1,NULL);
  pthread_create(&t2,NULL,(void *)HandleData2,NULL);
  pthread_create(&t3,NULL,(void )ReadData1,NULL);
  pthread_create(&t4,NULL,(void )ReadData2,NULL);
  /
防止程序过早退出,让它在此无限期等待
/
  pthread_join(t1,NULL);
}

  在Linux下,我们用命令gcc -lpthread sem.c -o sem生成可执行文件sem。 我们事先编辑好数据文件1.dat和2.dat,假设它们的内容分别为1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ,我们运行sem,得到如下的结果: 
    Multiply:-1*-2=2 
    Plus:-1+-2=-3 
    Multiply:9*10=90 
    Plus:-9+-10=-19 
    Multiply:-7*-8=56 
    Plus:-5+-6=-11 
    Multiply:-3*-4=12 
    Plus:9+10=19 
    Plus:7+8=15 
    Plus:5+6=11 

  从中我们可以看出各个线程间的竞争关系。而数值并未按我们原先的顺序显示出来这是由于size这个数值被各个线程任意修改的缘故。这也往往是多线程编程要注意的问题。 

 小结 

   多线程编程是一个很有意思也很有用的技术,使用多线程技术的网络蚂蚁是目前最常用的下载工具之一,使用多线程技术的grep比单线程的grep要快上几倍,类似的例子还有很多。希望大家能用多线程技术写出高效实用的好程序来。


#    fork进程(// IPC)
##  进程是资源的集合 
线程使用进程的资源
* 是Linux中的事务管理的基本单元 
* 进程是独立的,子进程完全的复制父进程,依靠返回值判断子进程与父进程
* getpid()获得进程的进程号
* getppid()获得进程的父进程的进程号
* getpgid()进程组号 

> 会话
> * 会话(Session)是一个或多个进程组的集合。一个会话可以有一个控制终端。建立与控制中短的链接的会话收进程被称为控制进程
> * pid_t getsid(pid_t pid);  返回进程会话号 == 进程组号
> * setsid(void);       

会话

![ 5中IO模型](https://img-blog.csdn.net/20180422154504541?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfNDA5Mjc3ODk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
##  控制终端
* 一个会话可以有一个控制终端 ,控制进程就是打开终端的进程,终端发送信号,只有前台进程组才能收到 
 


# 线程中的异步(线程中的信号)
* 区别进程的信号
* 每个线程可以向其他线程发送信号
* 每个信号都有信号屏蔽集合
* 同进程中所有线程贡献某线程的信号处理方式
* 不同进程的线程可以发送信号

* 线程所属进程()
---
* 线程属性
	typedef struct _pthread_attr_s{}
通常默认
例子 * 线程绑定在 服务器核
(tss端 , 代码程序的保存和恢复)






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值