线程与进程

Unix 从其产生伊始就将进程作为它的重要组成部分而线程是后来才加进去的。进程的概念非常清晰且统一。而线程却有着一系列的起源,它们的属性也各不相同。进程与线程有根本上的不同。每个进程有其独立的数据空间、文件描述符以及进程的ID 。而线程共享一个数据空间、文件描述符以及进程 ID 。
(1)共享数据空间
这里考虑一个在存储器中存储了巨大而复杂的树结构数据库的数据库系统。多个线程可以轻易地读取到这个共享的数据集。客户的多个查询可以由一个进程来实现。如果变量不会被改变,共享这个数据空间不会导致任何问题。再考虑一个使用 malloc 和 free 系统调用来管理内存的程序。一个线程分配了一块空间存储-个字符串。当此线程做其他事情的时候,另一个线程使用 free 释放了这块空间。那么原先的线程中本来指向此空间的指针现在指向了一块已经被释放的地方,更糟糕的是,这
块地方已经被派上别的用处了。线程机制还会带来内存的囤积。程序员往往因为怕影响了某线程正在使用的内存空间,只分配而不释放存储区域。这直接导致了内存的囤积,使用完毕也得不到释放。在单线程环境中返回指向静态局部变量的指针的函数无法兼容于多线程环境。因为同样的函数可能在多个线程中同时被调用而导致结果出错。简而言之,如果共享的变量很多且定义的不好,调试一个多线程的应用程序将会是噩梦一场。
(2) 共享的文件描述符
在 fork 原语被调用之后,文件描述符自动地被复制,从而子进程得到了一套新的文件描述符。在子进程关闭了某一个从父进程那里继承来的文件描述符之后,此描述符对父进程来说仍然是打开的。在多线程程序中,很有可能会将同一个文件描述符传递给两个不同的线程。即传递给它们的两个值指向同一个文件描述符。显然如果一个线程中的函数关闭了这个文件,此文件描述符对此进程中的任何线程来说都已经被关闭。然而其他线程或许仍然需要对此文件描述符的连接。
(3) fork 、 exec 、 exit 、 Signals
所有的线程都共享同一个进程。如果一个线程调用了 exec ,系统内核用一个新的程序取代当前的程序从而所有正在运行的线程都会消失。并且如果一个线程执行了 exit ,那么整个进程都将结束运行。想想要是线程的运行导致了内存段异常或者系统错误或是线程崩溃,瘫痪的是整个进程,而不是某个线程本身了。fork 创建了一个新的进程,并把原调用进程的数据和代码复制给这个新的进程。如果线程中的某函数调用了 fork,那么其他线程是不会被复制给新进程的。只有调用 fork 的线程在新的进程中运行。如果在 fork 发生的时刻,另一个线程正在修改数据,会发生不可预测的情况。

结论:将创建线程的代码放在fork以后。也就是放在新的子进程中进行创建
条件变量:
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

条件变量的处理过程:
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程
自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步
pthread_cond_init(pthread_cond_t *restrict cond,constpthread_condattr_t *restrict attr);
pthread_cond_wait (pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
pthread_cond_destory(pthread_cond_t *cond);
pthread_cond_signal (pthread_cond_t *__cond)
信号量 (SignaD 的使用要比线程复杂多了。进程可以接收任何种类的信号量。
pthread_cond_ wait 使线程挂起直到另一个线程通过条件变量发出消息。 pthread_ cond_wait 函数总是和互斥锁在一起使用。此函数先自动释放指定的锁,然后等待条件变量的变化。如果在调用此函数之前,互斥量 mutex 并没有被锁住,函数执行的结果是不确定的

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

pthread_mutex_t mutex;
pthread_cond_t cond;
int global = 1;

void * thr_func(void * args)
{
  int *i = (int *)args;
  while(1)
  {
pthread_mutex_lock(&mutex);
    while(global ==1)
    {
    pthread_cond_wait(&cond,&mutex);
    }
    global = 1;
    if(*i < 100)
    {
      usleep(1);
      printf("this is child!--[%d]\n",(*i)++);
    }
    //pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond);
  }
}

int main()
{
  pthread_t tid;
  int ret,i = 0;

  pthread_mutex_init(&mutex,NULL);
  pthread_cond_init(&cond,NULL);

  ret = pthread_create(&tid,NULL,thr_func,(void *)&i);
  if(ret != 0)
  {
    printf("pthread create error!\n");
    return -1;
  }
  sleep(1);
  while(1)
  {
    pthread_mutex_lock(&mutex);
    while(global == 0)
    {
    pthread_cond_wait(&cond,&mutex);
    }
    global = 0;
    if(i < 100)
    {
      usleep(1);
      printf("this is main! -- [%d]\n",i++);
    }

    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond);
  }
  return 0;
}   

这里写图片描述

这里写图片描述

pthread_cond_wait()分三步:
分别是解锁,阻塞,(唤醒后)加锁。
使用线程的优点:
信号处理者和定时器的机制虽然可以完成工作,但线程机制更好地匹配了内部和外部的结构

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值