进程、线程

并行、并发、进程、线程、多进程并发、多线程并发、多进程同步、多进程通讯、多线程同步、多进程通讯

在这里插入图片描述
并发:两个或多个事件同一时间段发生。
并行:两个或多个事件同一时刻发生。
进程:进程是指一个内存中运行中的应用程序。每个进程都有自己独立的一块内存空间(2GB),一个应用程序可以同时启动多个进程。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
线程:线程是CPU调度的最小单位(程序执行流的最小单元),它被包含在进程之中,是进程中的实际运作单元。一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程和线程都有就绪、阻塞和运行三种基本状态。
进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。多线程执行效率高;多进程耗资源,安全。
线程与进程的关系
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
(2)资源分配给进程,同一进程内的所有线程共享该进程的所有资源;
(3)线程在执行过程中需要协作同步。不同进程中的线程之间要利用消息通信的方法实现同步;
(4)处理机分配给线程,即真正在处理机上运行的是线程;
(5)线程是进程的一个执行单元,也是进程内的可调用实体。

多进程并发:进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的(静态的),进程是活的(动态的)。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;所有由用户启动的进程都是用户进程。进程是操作系统进行资源分配的单位。进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。
多线程并发:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单元。在单一程序中同时运行多个不同的工作,称为多线程。 多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率。线程是在同一时间需要完成多项任务的时候被实现的。

进程通讯:进程通信是指在进程间传输数据(交换信息)。进程通信根据交换信息量的多少和效率的高低,分为低级通信(只能传递状态和整数值)和高级通信(提高信号通信的效率,传递大量数据,减轻程序编制的复杂度)。
常见的通信方式
①管道pipe:管道一般用于两个不同进程之间的通信。当一个进程创建一个管道,并调用fork()创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。
②命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
③消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
最常见的是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
④共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。(通过信号灯实现存储共享(类似“红灯停、绿灯行”))
⑤套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);基于TCP的socket编程是采用的流式套接字。
服务器端编程的步骤:
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:绑定套接字到一个IP地址和一个端口上(bind());
3:将套接字设置为监听模式等待连接请求(listen());
4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());
5:用返回的套接字和客户端进行通信(send()/recv());
6:返回,等待另一连接请求;
7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
客户端编程的步骤:
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:向服务器发出连接请求(connect());
3:和服务器端进行通信(send()/recv());
4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
⑥信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
按通信类型区分
共享存储器系统
1.基于共享数据结构的通信方式
(仅适用于传递相对少量的数据,通信效率低,属于低级通信)
2.基于共享存储区的通信方式
管道通信系统
管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件(pipe文件)管道机制需要提供一下几点的协调能力
1.互斥,即当一个进程正在对pipe执行读/写操作时,其它进程必须等待
2.同步,当一个进程将一定数量的数据写入,然后就去睡眠等待,直到读进程将数据取走,再去唤醒。读进程与之类似
3.确定对方是否存在
消息传递系统
1.直接通信方式
发送进程利用OS所提供的发送原语直接把消息发给目标进程
2.间接通信方式
发送和接收进程都通过共享实体(邮箱)的方式进行消息的发送和接收
客户机服务器系统
1.套接字(socket)通信标识型的数据结构是进程通信和网络通信的基本构件
基于文件型的 (当通信进程都在同一台服务器中)其原理类似于管道
基于网络型的(非对称方式通信,发送者需要提供接收者命名。通信双方的进程运行在不同主机环境下被分配了一对套接字,一个属于发送进程,一个属于接收进程)
2.远程过程调用

进程同步:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。
①文件和记录锁定:为避免两个进程间同时要求访问同一共享资源而引起访问和操作的混乱,在进程对共享资源进行访问前必须对其进行锁定,该进程访问完后再释放。这是UNIX为共享资源提供的互斥性保障。
②信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
线程同步:线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的。线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。
线程同步的方式和机制
临界区(Critical Section)、互斥对象(Mutex):主要用于互斥控制;都具有拥有权的控制方法,只有拥有该对象的线程才能执行任务,所以拥有,执行完任务后一定要释放该对象。
信号量(Semaphore)、事件对象(Event):事件对象是以通知的方式进行控制,主要用于同步控制!
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。它并不是核心对象,不是属于操作系统维护的,而是属于进程维护的。
总结下关键段:
1)关键段共初始化、销毁、进入和离开关键区域四个函数。
2)关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。
3)推荐关键段与旋转锁配合使用。
2、互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。
总结下互斥量Mutex:
1)互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。
2)互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。
3、信号量:信号量也是内核对象。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最 大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1 ,只要当前可用资源计数是大于0 的,就可以发出信号量信号。但是当前可用计数减小 到0 时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离 开的同时通过ReleaseSemaphore ()函数将当前可用资源计数加1 。在任何时候当前可用资源计数决不可能大于最大资源计数。
4、事件对象: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作
总结下事件Event
1)事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。
2)事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。
3)事件可以解决线程间同步问题,因此也能解决互斥问题。
线程通讯:线程的多种创建方式
1.C++线程创建过程
1.1包含头文件#include
1.2创建线程:条用thread类去创建一个线程对象。
注意点:创建一个线程,不做处理,会调用abort函数(异常处理函数)中止程序。
一个线程只能jion一次,不能重复jion。
1.3 join()函数加入汇合线程,阻塞主线程,等待子线程执行结束,才会回到主线程。
1.4 detch()函数,分离,打破依赖关系,当线程detch后,就不能够jion().
joinable()判断与当前线程是否可以做jion()或者detch().

#include<iostream>
#include<thread>
#include<windows.h>
Using namespace std;
void print()
{
  sleep(5000);
cout<<”子线程运行。。。”<<endl;
}
int main()
{
thread test1(print);
  test1.detach();
  //test1.jion();
  cout<<”主线程。。。”<<endl;
return 0;
}

2.通过类和对象

#include<iostream>
#include<thread>
Using namespace std;
class person{
public:
       void operator()()
{
cout<<”子线程启动。。。”<<endl;
}
};
  int main()
{
  person p;
  thread test1(p);
  test1.join();
  thread test2((p()));
  test2.jion();
  cout<<”主线程。。。”<<endl;
  return 0;
}

3.Lambda表达式创建线程

#include<iostream>
#include<thread>
Using namespace std;
int max(int a,int b)
{
return a>b?a:b;
}
  int main()
{
  int (*pmax)(int,int)=nullptr;
  pmax=[](int a,int b)->int {return a>b?a:b;};
  [](){cout<<”helloworld”<<endl;}();//Lambda表达式可以把定义,调用,返回值写在一起
  cout<<pmax(1,2)<<endl;
thread  test1([]{cout<<”子线程。。。”<<endl;});
test1.jion();
  cout<<”主线程。。。”<<endl;
  return 0;
}

4.带参的方式创建线程

#include<iostream>
#include<thread>
Using namespace std;
void print1(int &num)
{
cout<<”子线程。。。”<<num<<endl;
}
  int main()
{
  int num=0;
  thread test1(print1,ref(num));
  test1.join();
  cout<<”主线程。。。”<<endl;
  return 0;
}

5.带智能指针创建线程

#include<iostream>
#include<thread>
Using namespace std;
void print1(unique_ptr<int> ptr)
{
cout<<”子线程:”<<ptr.get()<<endl;
}
  int main()
{
  unique_ptr<int> ptr(new int(1000));
  cout<<”主线程:”<<ptr.get()<<endl;
  thread test1(print,move(ptr));
  test1.join();
  cout<<”主线程:”<<ptr.get()<<endl;//this_thread::get_id() 可以看到当前线程的id.
  return 0;
}

输出:主线程:1258F68
子线程:1258F68
主线程:0000000
6.通过类的成员函数创建线程

#include<iostream>
#include<thread>
Using namespace std;
class person{
public:
       void print(int &num)
{
uum=1001;
cout<<”子线程启动。。。”<<this_thread::get_id()<<endl;
}
};
  int main()
{
  person p;
int num=1007;
  thread test1(&person::print,p,ref(num));
  test1.join();
  cout<<”主线程。。。”<< this_thread::get_id()<<endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值