进程与线程编程并实现累加和计算

进程与线程编程并实现累加和计算

进程

  • 进程是程序执行时的一个实例。它包括程序的代码、数据和进程控制块等资源。
  • 每个进程都有自己的地址空间,相互之间独立,一个进程的数据通常无法直接被其他进程访问。
  • 进程之间的通信需要通过显式的IPC(Inter-Process Communication,进程间通信)机制,例如管道、信号、消息队列等。
  • 进程是系统分配资源的基本单位,每个进程都有自己的内存空间、文件描述符、环境变量等。
  • 进程的创建和销毁比较耗费系统资源,因为需要分配和释放大量资源。

线程

  • 线程是进程内的一个执行单元。一个进程可以包含多个线程,它们共享相同的地址空间和其他资源。
  • 多线程可以提高程序的并发性,因为多个线程可以在同一时间内执行不同的任务,从而提高程序的效率。
  • 线程之间的通信更加方便,因为它们共享相同的内存空间,可以直接读写共享变量。
  • 线程的创建和销毁比进程轻量级,因为它们共享相同的资源,创建和销毁的开销较小。

fork()函数介绍

所需头文件

#include<unistd.h>
#include<sys/types.h>

函数定义 : pid_t fork( void );

pid_t 是一个宏定义,其实质是int 被定义在#include

返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1

函数说明:

一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。父子进程间共享的存储空间只有代码段。

fork() 创建的新进程拥有父进程数据段、堆和栈的副本,但是这两个进程的这些部分在物理内存中是独立的。父子进程只共享代码段

代码示例:

#include<sys/types.h> 
#include<unistd.h> 
#include<stdio.h> 
#include<stdlib.h> 
#include<errno.h> 

int main() 
{ 

  pid_t pid;/*pid 进程id号*/ 

  pid=fork();/*创建一个新进程*/ 

  if(pid==0) /*返回0为子进程*/ 
  { 
   printf("Return pid is %d\n",pid); 
   printf("This is son process!  pid is:%d\n",getpid()); 

  } 
  else if(pid>0)/*返回大于0为父进程*/ 
  { 
    printf("Return pid is %d\n",pid); 
    printf("This is parent process!  pid is:%d\n",getpid()); 
     waitpid(pid,NULL,0);/*等待子进程退出*/
  } 
  else 
  { 
     perror("fork() error!"); 
     exit; 
  } 
}

pthread_create()函数的使用

pthread_create() 是 POSIX 线程库中用于创建新线程的函数。POSIX 线程,或 “pthread”,是一个可移植的线程标准,定义了线程的创建、控制和终止等操作。pthread_create() 调用会创建一个新的线程并将其加入到当前进程中。新线程从指定的函数地址开始执行。

#include<pthread.h>
int pthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict_attr,
                    void*(*start_rtn)(void*),void *restrict arg);

参数:

  • 第一个参数为指向线程标识符的指针。
  • 第二个参数用来设置线程属性。一般为NULL,表示默认属性
  • 第三个参数是线程运行函数的起始地址。
  • 最后一个参数是运行函数的参数。

另外,在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库

返回:

若成功则返回0,否则返回出错编号

返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。

linux下用C开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。

由 restrict 修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。对对象的存取都限定于基于由 restrict 修饰的指针表达式中。 由 restrict 修饰的指针主要用于函数形参,或指向由 malloc() 分配的内存空间。restrict 数据类型不改变程序的语义。 编译器能通过作出 restrict 修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。

代码示例:

#include <iostream>
#include <pthread.h>
using namespace std;
pthread_t thread;
void fn(void *arg)
{
    int i = *(int *)arg;
    cout<<"i = "<<i<<endl;
    return ((void *)0);
}
int main()
{
    int err1;
    int i=10;
    err1 = pthread_create(&thread, NULL, fn, &i);
    pthread_join(thread, NULL);
}

多进程实现累加计算数列和

fork()创建子进程,每个子进程计算数列的一部分,父进程收集计算结果

父子进程之间的通信:管道(pipe)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>

#define NUM_PROCESSES 10 // 定义子进程数量

// 累加函数,每个子进程执行的代码
long sumRange(int start, int end) {
    long sum = 0;
    for (int i = start; i <= end; ++i) {
        sum += i;
    }
    return sum;
}

int main() {
    int numbersPerProcess = 1000000000 / NUM_PROCESSES;
    int pipefds[2 * NUM_PROCESSES]; // 为每个子进程创建一个管道
    pid_t pids[NUM_PROCESSES];
    struct timeval start, end;

    // 获取开始时间
    gettimeofday(&start, NULL);

    // 创建管道
    for (int i = 0; i < NUM_PROCESSES; ++i) {
        if (pipe(pipefds + i*2) == -1) {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
    }

    // 创建子进程
    for (int i = 0; i < NUM_PROCESSES; ++i) {
        pids[i] = fork();
        if (pids[i] < 0) {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (pids[i] == 0) { // 子进程
            close(pipefds[i*2]); // 关闭读端

            int start = i * numbersPerProcess + 1;
            int end = (i + 1) * numbersPerProcess;
            long partialSum = sumRange(start, end);

            write(pipefds[i*2 + 1], &partialSum, sizeof(partialSum)); // 写入计算结果
            close(pipefds[i*2 + 1]); // 关闭写端

            exit(EXIT_SUCCESS);
        }
    }

    // 父进程
    long totalSum = 0, readSum = 0;

    // 等待子进程并读取其计算结果
    for (int i = 0; i < NUM_PROCESSES; ++i) {
        close(pipefds[i*2 + 1]); // 关闭写端
        read(pipefds[i*2], &readSum, sizeof(readSum));
        totalSum += readSum;
        close(pipefds[i*2]); // 关闭读端

        wait(NULL); // 等待子进程结束
    }

    // 获取结束时间
    gettimeofday(&end, NULL);

    // 计算并打印执行时间
    long seconds = end.tv_sec - start.tv_sec;
    long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);

    printf("NUM_PROCESSES = %d\n", NUM_PROCESSES);
    printf("Total Sum = %ld\n", totalSum);
    printf("Time taken: %ld microseconds (%.3f seconds)\n", micros, micros / 1000000.0);

    return 0;
}

多线程实现累加和计算

使用多线程实现累加和计算,我们可以将要累加的数值范围分割给多个线程,每个线程计算自己那部分的和,最后将所有线程的结果累加起来得到最终结果。这里,我们将使用 POSIX 线程库(pthread)来实现这个多线程累加和计算。

以下是一个简单的实现方案:

  1. 定义一个结构体来传递给每个线程,这个结构体包含了线程需要知道的信息,比如计算的起始和结束值。
  2. 创建多个线程,每个线程负责计算一部分数值的和。
  3. 等待所有线程完成,然后汇总每个线程计算的结果。
  4. 输出最终的累加和
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define NUM_THREADS 10 // 定义线程数量

typedef struct {
    int start;
    int end;
    long sum; // 用于存储这个线程计算的部分和
} ThreadData;

// 线程函数
void* sumRange(void* arg) {
    ThreadData* data = (ThreadData*)arg;
    data->sum = 0;
    for (int i = data->start; i <= data->end; ++i) {
        data->sum += i;
    }
    pthread_exit((void*) &(data->sum));
}

int main() {
    pthread_t threads[NUM_THREADS];
    ThreadData threadData[NUM_THREADS];
    int numbersPerThread = 1000000000 / NUM_THREADS;
    long totalSum = 0;

    struct timeval start, end;

    // 获取开始时间
    gettimeofday(&start, NULL);

    // 创建线程
    for (int i = 0; i < NUM_THREADS; ++i) {
        threadData[i].start = i * numbersPerThread + 1;
        threadData[i].end = (i + 1) * numbersPerThread;
        pthread_create(&threads[i], NULL, sumRange, (void*)&threadData[i]);
    }

    // 等待线程完成并汇总结果
    for (int i = 0; i < NUM_THREADS; ++i) {
        void* status;
        pthread_join(threads[i], &status);
        totalSum += *(long*)status;
    }

    // 获取结束时间
    gettimeofday(&end, NULL);

    // 计算并打印执行时间
    long seconds = end.tv_sec - start.tv_sec;
    long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);

    printf("NUM_THREADS = %d\n", NUM_THREADS);
    printf("Total Sum = %ld\n", totalSum);
    printf("Time taken: %ld microseconds (%.3f seconds)\n", micros, micros / 1000000.0);

    return 0;
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值