Linux多线程编程

任务1:

要求:编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。各对等线程的输出操作是:

  1. T1:输出“My name is <您的姓名xxx>”
  2. T2:输出“My student number is <您的学号xxx>”
  3. T3:输出“Current time <当前时间,包括年月日时分秒>

要求:

  1. 采用文件复制、文本复制或输入方式在Linux工作目录下创建源程序文件。
  2. 编译、调试、运行程序,观察输出结果

设计思想:

用随机数函数rand()来生产1-5之间的随机数,用时间结构体与时间格式函数strftime来记录当前时间与规定时间格式。

#include "wrapper.h"
void *C1(void *no);
void *C2(void *no);
void *C3(void *no);
int main()
{
    pthread_t T1, T2, T3;
    pthread_create(&T1, NULL, C1, NULL);
    pthread_create(&T2, NULL, C2, NULL);
    pthread_create(&T3, NULL, C3, NULL);
    pthread_join(T1, NULL);
    pthread_join(T2, NULL);
    pthread_join(T3, NULL);
}
void *C1(void *no)
{
    int i = 0;
    while (i < 5)
    {
        printf("My name is 泠霖凛\n");
        sleep(rand() % 5 + 1);
        i++;
    }
}
void *C2(void *no)
{
    int i = 0;
    while (i < 5)
    {
        printf("My student number is 201944101126\n");
        sleep(rand() % 5 + 1);
        i++;
    }
}
void *C3(void *no)
{
    time_t t;
    struct tm *newtime;
    char str[100];
    int i = 0;
    while (i < 5)
    {
        t = time(NULL);
        newtime = localtime(&t);
        strftime(str, 100, "<%Y年%m月%d日%H时%M分%S秒>\n", newtime);
        printf("Current time%s", str);
        sleep(rand() % 5 + 1);
        i++;
    }
}

任务2:

要求:编译、测试和运行图6-13示例程序badcount.c,请通过测试找到程序运行开始出错的niters的最小值,并解释出错原因。用pthread信号量方法改写程序badcount.c,保存为task62.c,实现对共享变量的安全访问,并进行测试验证。

要求:

  1. 用niters=10n进行测试,给出程序运行出错与n的关系,解释出错原因;
  2. 采用文件复制、文本复制或输入方式在Linux工作目录下创建源程序文件task62.c。
  3. 编译、调试、运行程序,观察输出结果
#include "wrapper.h"
void *increase(void *arg);
void *decrease(void *arg);
sem_t in;
int cnt = 0;
int main(int argc, char **argv)
{
    unsigned int niters;
    pthread_t tid1, tid2;
    if (argc != 2)
    {
        printf("usage:%s <niters>\n", argv[0]);
        exit(2);
    }
    niters = atoll(argv[1]);
    sem_init(&in, 0, 1);
    pthread_create(&tid1, NULL, increase, (void *)niters);
    pthread_create(&tid2, NULL, decrease, (void *)niters);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    if (cnt != 0)
        printf("Error! cnt=%d\n", cnt);
    else
        printf("Correct cnt=%d\n", cnt);
    exit(0);
}
void *increase(void *vargp)
{
    unsigned int i, niters = (unsigned int)vargp;
    for (i = 0; i < niters; i++)
    {
        sem_wait(&in);
        cnt++;
        sem_post(&in);
    }
    return NULL;
}
void *decrease(void *vargp)
{
    unsigned int i, niters = (unsigned int)vargp;
    for (i = 0; i < niters; i++)
    {
        sem_wait(&in);
        cnt--;
        sem_post(&in);
    }
    return NULL;
}

任务3:

要求:编写一个多线程程序task63.c,创建k个生产者线程和m个消费者线程,每个生产者线程产生若干个随机数,通过由N个单元构成的缓冲区,发送给消费者线程,进行输出显示,产生Pthread信号量实现生产者/消费者线程间同步,并且设计一种方案对程序正确性进行验证。

    提示:一种非严谨的简单验证方案是,将生产者线程产生的所有随机数相加得到一个和,消费者线程接收到的所有随机数相加得到另一个和,验证两个和是否一致来来验证程序正确性。

设计思想:

    通过rand()函数生成范围在1-10的随机数。当消费者获取所有生产者生产的数后,通过pthread_cancel函数杀死剩余的消费者,然后检验生产的数和消费的数是否相同。

#include "wrapper.h"
#define K 10
#define M 20
#define N 20
int consum = 0;
int prosum = 0;
int buf[N], n, flag = K;
void *consumer(void *no);
void *producer(void *no);
sem_t avail, ready;
int main()
{
    pthread_t con[K], pro[M];
    sem_init(&avail, 0, 1);
    sem_init(&ready, 0, 0);
    for (int i = 0; i < K; i++)
        pthread_create(&con[i], NULL, consumer, NULL);
    for (int i = 0; i < M; i++)
        pthread_create(&pro[i], NULL, producer, &i);
    for (int i = 0; i < K; i++)
        pthread_join(con[i], NULL);
    while (1)
    {
        if (flag == 0)
        {
            for (int i = 0; i < M; i++)
                pthread_cancel(pro[i]);
            break;
        }
    }
    sem_destroy(&avail);
    sem_destroy(&ready);
    printf("生产者生产了%d\n", consum);
    printf("消费者消费了%d\n", prosum);
    if (consum == prosum)
        printf("正确!\n");
    else
        printf("错误!\n");
}
void *consumer(void *no)
{
    sem_wait(&avail);
    int num;
    n = 0;
    int sum = rand() % 20 + 1;
    for (int i = 0; i < sum; i++)
    {
        num = rand() % 10 + 1;
        consum += num;
        buf[n++] = num;
    }
    sem_post(&ready);
}
void *producer(void *no)
{
    sem_wait(&ready);
    int id = *((int *)no);
    printf("%d号消费者:", n);
    for (int i = 0; i < n; i++)
    {
        printf("%d ", buf[i]);
        prosum += buf[i];
    }
    printf("\n");
    flag--;
    sem_post(&avail);
}

任务4:

要求:编译、测试和运行示例程序psum64.c

  1. 测量线程数为1、2、4、8、16时程序的执行时间,计算加速比和效率,并做出解释。

线程(t)

1

2

4

8

16

核(p)

1

2

4

4

4

运行时间(Tp)

2.51

1.28

0.64

0.47

0.46

加速比(Sp)

1

1.96

3.94

5.34

5.46

效率(Ep)

100%

98%

98.5%

66.8%

34.1%

随着线程数的增加,运行时间减少。增加到4个线程后,运行时间趋于平稳。但增加到16个线程后,运行时间反而呈现增加的趋势,这是由于多个线程被分配到同一个核上,从而增加了线程上下文切换的开销。

2)改写该程序psum64.c,保存为task64.c,实现计算02+12+… +(n-1)2功能。

要求:

计算不同线程数时的性能,填写以下表格,并对运行时间和加速比进行解释:

线程(t)

1

2

4

8

16

核(p)

1

2

4

4

4

运行时间(Tp)

0.016

0.011

0.007

0.007

0.008

加速比(Sp)

1

1.45

2.29

2.29

2

效率(Ep)

100%

72.5%

57.25%

28.62%

12.5%

#include "wrapper.h"
#define MAXTHREADS 32
void *sum(void *vargp);
unsigned long long psum64[MAXTHREADS];
unsigned long long nelems_per_thread;
int main(int argc, char **argv)
{
    unsigned long long i, nelems, log_nelems, nthreads, result = 0;
    pthread_t tid[MAXTHREADS];
    int myid[MAXTHREADS];
    if (argc != 3)
    {
        printf("Usage:%s<ntheads><log_nelems>\n", argv[0]);
        exit(0);
    }
    nthreads = atoi(argv[1]);
    log_nelems = atoi(argv[2]);
    nelems = (1LL << log_nelems);
    nelems_per_thread = nelems / nthreads;
    for (i = 0; i < nthreads; i++)
    {
        myid[i] = i;
        pthread_create(&tid[i], NULL, sum, &myid[i]);
    }
    for (i = 0; i < nthreads; i++)
        pthread_join(tid[i], NULL);
    for (i = 0; i < nthreads; i++)
        result += psum64[i];
    if (result == (nelems * (nelems - 1) * (2 * nelems - 1) / 6))
        printf("Corrrct:reasult=%ld\n", result);
    else
        printf("Error:result=%ld\n", result);
    exit(0);
}
void *sum(void *vargp)
{
    int myid = *((int *)vargp);
    unsigned long long begin = myid * nelems_per_thread;
    unsigned long long end = begin + nelems_per_thread;
    unsigned long long i, lsum = 0;
    for (i = begin; i < end; i++)
        lsum += i * i;
    psum64[myid] = lsum;
    return NULL;
}

任务5:

要求:编写一个N×N矩阵乘法函数的并行线程化版本,程序保存为matmult.c,设计一种方案,验证并行程序正确性。编译、调试、运行程序,设2k≤N<2k+1给出线程数为1、2、4、…、2k-1、2k等情况下的运行时间,计算加速比与效率,并对结果给出解释。

线程数(t)

1

2

4

2k-1

2k

核(p)

1

2

4

4

4

运行时间(Tp)

0.46

0.35

0.31

0.29

0.29

加速比(Sp)

1

1.31

1.48

1.59

1.59

效率(Ep)

100%

65.5%

37%

1.59/2k-1*100%

1.59/2k*100%

提示:可将输出结果与正确串行版本计算结果进行对比来验证程序正确性

设计思想:通过线程的序号来确定当前线程所需计算的范围,用len来确定每个线程所需计算的量

#include "wrapper.h"
#define N 5000
int A1[N][N];
int A2[N][N];
unsigned long long len;
void *sum(void *no);
int main(int argc, int **argv)
{
    unsigned long long i, j, ph = atoi(argv[1]);
    pthread_t co[ph];
    len = N / ph;
    for (i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            A1[i][j] = rand() % 10 + 1;
    for (i = 0; i < ph; i++)
        pthread_create(&co[i], NULL, sum, &i);
    for (i = 0; i < ph; i++)
        pthread_join(co[i], NULL);
}
void *sum(void *no)
{
    unsigned long long i, j, pd = *((int *)no);
    for (i = pd * len; i < pd * len + len; i++)
        for (j = 0; j < N; j++)
            A2[i][j] = A1[i][j] * A1[j][i];
    return NULL;
}

任务6:

要求:在Linux环境下,可以调用库函数gettimeday测量一个代码段的执行时间,精度可达微秒级,请写一个程序task66.c,测量和比较fork、pthread_create函数调用所需的执行时间,并进行解释。

提示:可以多次反复调用读写函数,计算累积时间

设计思想:使用gettimeofday函数得到当前时间

#include "wrapper.h"
void *cr(void *vargp) {}
int main()
{
    pid_t p[100];
    pthread_t t[100];
    struct timeval tp;
    struct timeval tp1;
    double g_time_start;
    double g_time_end;
    gettimeofday(&tp, NULL);
    g_time_start = tp.tv_sec * 1000000 + tp.tv_usec;
    for (int i = 0; i < 100; i++)
        pthread_create(&t[i], NULL, cr, NULL);
    gettimeofday(&tp1, NULL);
    g_time_end = tp1.tv_sec * 1000000 + tp1.tv_usec;
    printf("pthread_create函数的运行时间为:%.4f微秒\n", (g_time_end - g_time_start) / 100);
    gettimeofday(&tp, NULL);
    g_time_start = tp.tv_sec * 1000000 + tp.tv_usec;
    for (int i = 0; i < 100; i++)
        if ((p[i] = fork()) == 0)
            exit(0);
    gettimeofday(&tp1, NULL);
    g_time_end = tp1.tv_sec * 1000000 + tp1.tv_usec;
    printf("fork函数的运行时间为:%.4f微秒\n", (g_time_end - g_time_start) / 100);
    for (int i = 0; i < 100; i++)
    {
        waitpid(p[i], NULL, WNOHANG);
        pthread_join(t[i], NULL);
    }
    exit(0);
}

  • 30
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 多线程编程是一种技术,可以让一个程序并行地处理多个任务。它可以提高程序的执行效率,更有效地使用计算机的资源,并减少系统响应时间。Linux 是一种流行的操作系统,支持多线程编程多线程编程是指在同一进程中同时执行多个线程每个线程执行自己的任务。下面是一些关于 Linux 多线程编程的基本概念和技术: 1. 线程和进程的区别:线程是进程内的一个执行单元,进程是操作系统分配资源的一个独立单位。 2. 线程同步:线程同步是指协调多个线程之间的执行顺序,防止出现数据竞争和死锁等问题。常见的线程同步技术包括互斥量、条件变量和信号量等。 3. 线程池:线程池是一种预先创建一组线程并重复使用的技术。线程池可以提高多线程程序的效率和性能。 4. POSIX 线程库:POSIX 线程库是 Linux 操作系统支持的一种多线程编程接口,提供了一套标准的多线程 API,包括创建、销毁、同步和调度线程等功能。 5. 多线程调试:多线程程序的调试需要注意避免数据竞争和死锁等问题,可以使用调试工具和技术,如 gdb 和 Valgrind 等。 总之,多线程编程Linux 程序员必备的技能之一,掌握多线程编程技术可以提高程序的效率和性能,同时也需要注意避免常见的线程问题。 ### 回答2: Linux 多线程编程是一种在 Linux 操作系统上开发并行应用程序的方式,它允许一个程序同时执行多个线程,从而提高程序的响应速度和运行效率。在 Linux 中,线程是轻量级的进程,它们共享同一进程的资源和数据,可以同时运行在不同的 CPU 核心上,使得程序在多核系统中具有更好的性能表现。 Linux 提供了多种多线程编程的 API,其中最常用的是 pthreads 库,它是一组 C 语言函数,可用于创建、同步和管理多个线程。使用 pthreads 库编写多线程程序的基本步骤包括定义线程函数、创建线程执行线程、同步线程和销毁线程,这些步骤需要程序员显式地调用相关的 API 函数来实现。 在编写多线程程序时,必须考虑线程之间的共享资源和同步问题。共享资源包括程序的数据、文件、网络连接等,可以使用临界区、互斥锁、信号量等技术来保护。同步问题则是确保多个线程之间按照正确的顺序执行,不会产生死锁、饥饿等问题,可以使用信号量、互斥锁、条件变量等技术来实现。 此外,多线程编程还需要考虑到线程的调度问题,即如何让不同的线程在不同的时间片内执行,从而实现线程的抢占和优先级控制。Linux 提供了优先级调度器和时间片分配器来实现线程的调度,程序员可以根据需要设定优先级和时间片长度来控制线程执行顺序和时间切片。 总之,Linux 多线程编程是一种高效、灵活和可扩展的编程模型,能够充分利用多核系统的性能,并实现程序的并行化和异步化。开发者需要熟悉多线程编程的基本概念、API 和技术,遵循正确的资源共享和同步策略,才能编写出高质量、可靠、并发的多线程程序。 ### 回答3: Linux 多线程编程是指在 Linux 操作系统下使用多个线程同时运行程序来提高程序的运行效率、并实现多任务处理。多线程编程具有使用方便、管理灵活、响应速度相对于进程更快的优势,常被应用于高并发服务器中。 在 Linux 中,多线程编程的实现要依赖于 pthread 库。该库提供了一些线程函数和数据结构,用于创建、控制、同步和分离线程等操作。以下是一些常用的 pthread 函数: 1. pthread_create():用于创建一个新线程; 2. pthread_join():线程阻塞等待子线程结束; 3. pthread_exit():用于在线程代码中退出线程; 4. pthread_mutex_lock():线程加锁; 5. pthread_mutex_unlock():线程解锁; 6. pthread_cond_wait():线程等待; 7. pthread_cond_signal():唤醒等待的线程。 除了 pthread 库,Linux 还提供了其它一些可用于多线程编程的工具和技术,如: 1. OpenMP:一种基于共享内存的多线程编程模型; 2. MPI:一种消息传递的分布式并行编程模型。 当进行多线程编程时,需要注意以下几个方面: 1. 多线程编程的并发性可能会导致一些竞态条件,需要使用锁或互斥量进行同步; 2. 在实现多线程编程过程中,应该尽量避免使用全局变量或静态变量; 3. 在多线程编程实践中,应注意资源的释放和内存泄漏的问题。 总之,Linux 多线程编程是一项非常重要的技能,它能够在提高程序性能和响应速度方面发挥重要作用。熟练掌握多线程编程技术,对于编写高效稳定的服务端程序和优化多线程应用程序的性能都非常有益。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值