1. 程式人生 > 實用技巧 >簡單多執行緒程式設計中的切換問題(os-hw4)

簡單多執行緒程式設計中的切換問題(os-hw4)

寫一個 2 執行緒的程式:

首先生成一個從 1 到 1000 萬的整數陣列,然後用兩個執行緒分別計算陣列奇數部分和偶數部分的和,並打印出總的和。(採用 pthread API )

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
#define N 10000000
#define STEP 2

long int integer[N];

void *sumf(void *t)
{
    extern long int integer[N];
    int ini = *(int *)t;
    if (integer[ini] % STEP)
        printf("odd sum");
    else
        printf("even sum");
    long int s = 0;
    for (int i = ini; i < N; i += STEP)
    {
        s = s + integer[i];
    }
    printf(": %ld\t", s);
}

int main()
{
    // array of integers
    extern long int integer[N];
    for (int i = 0; i < N; i++)
        integer[i] = i + 1;
    // sample output
    long int s_spl = 0;
    for (int i = 0; i < N; i += STEP)
        s_spl += integer[i];
    printf("sample:\todd sum: %ld", s_spl);
    s_spl = 0;
    for (int i = 1; i < N; i += 2)
        s_spl += integer[i];
    printf("\teven sum: %ld\n", s_spl);

    // 2 threads
    printf("2 threads:\n\t");
    pthread_t th1, th2;
    // time1
    struct timeval t0, t1;
    time_t sec0;
    suseconds_t msec0;
    gettimeofday(&t0, NULL);

    int para0 = 0, para1 = 1;
    pthread_create(&th1, NULL, sumf, (void *)&para0);
    pthread_create(&th1, NULL, sumf, (void *)&para1);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    // time2
    gettimeofday(&t1, NULL);
    sec0 = t1.tv_sec - t0.tv_sec;
    msec0 = t1.tv_usec - t0.tv_usec;
    printf("\n\t(glibc) time used:%ld.%ldms\n", sec0 * 1000, msec0);
    return 0;
}

編譯執行的結果:

$ gcc hw4.c -lm -O3 -g -o hw4 -lpthread # 在最後連結pthread的動態庫
$ ./hw4    
sample: odd sum: 25000000000000 even sum: 25000005000000
2 threads:
        odd sumeven sum: 25000000000000 : 25000005000000
        (glibc) time used:0.4862ms

可以發現最後輸出格式略奇怪,檢查 sumf 函式,發現它的結構大致為:if-calc()-prtf(res),由於 pthread_create 產生的執行緒可能在執行中隨機切換(即執行緒內有序,執行緒間隨機),因此有可能執行完 th1 的 if 段後切換到 th2 執行,從而使輸入格式不符合預期(在程式上並沒有毛病)。

為了改善這種情況,注意到 printf 提供的是封裝的核心態函式的使用者態介面,因此考慮把 if 的 print 和 res 的print合併使用 printf,新的 sumf 如下:

void *sumf(void *t)
{
    extern long int integer[N];
    int ini = *(int *)t;
      // 把求和放在前面
    long int s = 0;
    for (int i = ini; i < N; i += STEP)
    {
        s = s + integer[i];
    }
      // 合併輸出想要的結果
    if (integer[ini] % STEP)
        printf("odd sum: %ld\t", s);
    else
        printf("even sum: %ld\t", s);
}

得到的結果即修正為:

$ gcc hw4.c -lm -O3 -g -o hw4 -lpthread
$ ./hw4
sample: odd sum: 25000000000000 even sum: 25000005000000
2 threads:
        odd sum: 25000000000000 even sum: 25000005000000
        (glibc) time used:0.4958ms

該修改的效用得到驗證。