进程

线程是资源分配的最小单元,拥有代码,数据,文件,网络连接,地址空间等资源。

fork

fork()是一个系统调用,用于创建进程。创建的这个进程与原来进程几乎完全相同。这个新产生的进程称为子进程。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

需要注意的一点:就是调用fork之后,两个进程同时执行的代码段是fork函数之后的代码,而之前的代码已经由父进程执行完毕。下面来看一个很简单的例子。

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. int main()
  4. {
  5. pid_t pid;
  6. int count = 0;
  7. pid = fork(); //fork一个进程
  8. if(pid == 0) { //pid为0,
  9. printf("this is child process, pid is %d\n",getpid());//getpid返回的是当前进程的PID
  10. count+=2;
  11. printf("count = %d\n",count);
  12. } else if(pid > 0) {
  13. printf("this is father process, pid is %d\n",getpid());
  14. count++;
  15. printf("count = %d\n",count);
  16. } else {
  17. fprintf(stderr,"ERROR:fork() failed!\n");
  18. }
  19. return 0;
  20. }

在main()函数调用fork了,创建了一个新的进程,这个进程称为原来进程的子进程。子进程与原来的进程并发执行,谁先谁后没有规律,由操作系统调度决定。

其实是子进程使用了和父进程一样的页表,导致两个进程所有数据都是一模一样的,没有任何差别。当然,在子进程的页表里会有特殊标记,使得当子进程需要写这段内存时(读的时候任何事情都不会发生),内核会将要写的这一页复制一份新的给子进程。是谓「写时复制」

线程

线程是一个执行和调度的单元,一个线程就是不停地去读取指令,解码,执行,写回。CPU 就是以线程为单位执行的。同一个进程中的线程共享其他线程的资源。拥有独立的寄存器和栈(函数调用需要)。

C++ 多线程方案有 pthread 和 c++11 标准库中的 thread,这里介绍标准库的 thread ,thread 构造函数只要传入一个可以调用的对象即可。

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <thread>
  5. #include <unistd.h>
  6. #include <random>
  7. #include <deque>
  8. constexpr size_t COUNT = 4 * 1024 * 1024;
  9. int main () {
  10. using namespace std::chrono;
  11. std::vector<double> vectors[4];
  12. std::default_random_engine e;
  13. std::uniform_real_distribution<double> u(0.0, 1.0);
  14. for (auto &vec : vectors) {
  15. vec.resize(COUNT);
  16. std::generate(vec.begin(), vec.end(), [&e, &u]() {return u(e);});
  17. }
  18. auto start_time = high_resolution_clock::now();
  19. std::deque<std::thread> threads;
  20. for (auto &vec : vectors) {
  21. threads.emplace_back([&vec] () {
  22. double sum = 0;
  23. for (int i = 0; i < COUNT; ++i) {
  24. sum += vec[i];
  25. }
  26. std::cout << sum << std::endl;
  27. });
  28. }
  29. for (auto &t : threads) {
  30. t.join();
  31. }
  32. auto finish_time = std::chrono::high_resolution_clock::now();
  33. std::cout << std::chrono::duration_cast<std::chrono::microseconds>(finish_time - start_time).count() << "us" << std::endl;
  34. return 0;
  35. }

如果线程数大于 cpu 核数,那么在执行多线程的时候会发生上下文切换,此时的速度不如顺序执行的速度快。 taskset -a -c 1 target.exe 来使用一个 cpu 来执行这个程序。