1. 程式人生 > 其它 >多執行緒排序-覆盤

多執行緒排序-覆盤

除錯工具

valgrind記憶體洩漏檢測

參考連線:

GDB

time exe

程式碼實現

專案目標

  • 實現多執行緒排序

程式碼

/**
 * @file sort.c
 * @author binary (binary@techbootcamp)
 * @brief 多執行緒排序專案
 * @version 0.1
 * @date 2021-11-03
 * 
 * @copyright Copyright (c) 2021
 * 
 */
///////////////////////////////////////////////////////////////////////////////
//標頭檔案區域,所需要的標頭檔案,新增到這裡。

#include <stdio.h>

#include <stdlib.h> // exit()
#include <pthread.h>
#include <unistd.h>
#include <string.h>

/*
gcc -o sort sort.c -w -lpthread
./sort
*/
///////////////////////////////////////////////////////////////////////////////
//巨集定義

#define SIZE 500

#define thread_MAX 2

///////////////////////////////////////////////////////////////////////////////
//結構體

typedef struct
{
    int size;
    int data[SIZE];
} datas;
datas numbers;

typedef struct
{
    int start;
    int end;
} parameters;


///////////////////////////////////////////////////////////////////////////////
//變數宣告

///////////////////////////////////////////////////////////////////////////////
//函式宣告

/**
 * @brief 列印資料
 * 
 * @param array 
 */
void print_data(datas array);   // OK 

/**
 * @brief 檢測資料是否已經排序
 * 
 * @param array 
 * @return true 
 * @return false 
 */
int is_sorted(datas array);     // OK

/**
 * @brief 排序執行緒的回撥函式
 * 
 * @param params 
 * @return void* 
 */
void *sorter(void *params);

/**
 * @brief 合併執行緒的回撥函式
 * 
 * @param params 
 * @return void* 
 */
void *merger(void *params);

/**
 * @brief 從檔案中讀取資料。(原始資料,未排序的)
 * 
 */
void input_data();            // OK

/**
 * @brief 排序執行緒的初始化和工作過程
 * 
 */
void handle_sorting();        // OK

/**
 * @brief 合併執行緒的初始化和工作過程
 * 
 */
void handle_merge();

/**
 * @brief 顯示排序資料是否正確
 * 
 * 排序不正確,顯示排序不正確,並打印出排序資料。
 * 排序正確,則顯示排序正確。
 * 
 */
void show_data();           // OK

/**
 * @brief 輸出資料到檔案。(已經排好序的)
 * 
 */
void output_data();

/**
 * @brief 清理一些資源。比如動態記憶體分配的資源
 * 
 */
void cleanup();

//TODO:你的排序演算法函式在這裡宣告

///////////////////////////////////////////////////////////////////////////////
//函式實現

/**
 * @brief 主函式人口
 * 
 * @param argc 引數個數
 * @param argv 引數陣列
 * @return int 返回狀態
 */
int main(int argc, char *argv[])
{
    input_data();
    handle_sorting();
    handle_merge();
    show_data();
    output_data();
    cleanup();
    return 0;
}

// @brief 列印資料
void print_data(datas array)
{
    printf("列印資料:");
    for (int k = 0; k <= array.size - 1; k++)
    {
        printf("%d,", array.data[k]);
    }
    putchar('\n');
}

// @brief 檢測資料是否已經排序
int is_sorted(datas array)
{
    char flag = 0;
    if (array.size < 1)
    {
        printf("Input array is empty.\n");
        return 1;
    }

    for (int i = 1; i < array.size; i++)
    {
        if (array.data[i - 1] > array.data[i])
        {
                flag = 1;
        }
        if (flag)
        {
            return 1;
        }
    }
    return 0;
}

// @brief 排序執行緒的回撥函式
void swap(int *a,int *b) //交換兩個變數
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

/*  排序 */
void sort_select(int * data, int size)
{
    int i, j, min;
    
    if (data == NULL || size <= 0)
    {
        printf("data error,sort_select().\n");
    }
    
    for (i = 0; i < size; i++)      // 從第一個,到最後第二個,每個都要和後面所有比較
    {
        min = i;                    // 預設選第一個是最小值
        for (j = i + 1; j <= size; j++)     // 後面一次比較的迴圈
        {
            if (data[i] > data[j])
            {
                swap(&data[i], &data[j]);
            }
        }
    }
}

// @params:存放起止位置的指標,p=params
void *sorter(void *params)
{
    // 選擇排序
    parameters * p = (parameters *)params;
    // data指向儲存源陣列的開始位置
    int *data = &numbers.data[p->start];
    int size = p->end - p->start;   // 執行緒處理資料的大小

    //printf("size:%d.\n", size);
    sort_select(data, size);
}

void *merger(void *params)
{
    //printf("asdasdasd\n");
    parameters * p = (parameters *)params;
    int *data = &numbers.data[p->start];
    int size = p->end - p->start;   // 執行緒處理資料的大小
    sort_select(data, size);
}

void input_data()
{
    FILE *fp;
    char ch;
    char str[30] = {0}; 
    int i = 0, j = 0;
    
    numbers.size = 0;

    fp = fopen("sort", "r");        // 開啟數資料夾
    if (fp == NULL)                     // 如果為空資料夾
    {
        printf("Empty txt file.\n");
        exit(1);
    }

    while ((ch = fgetc(fp)) != EOF)                   // 迴圈處理檔案內容
    {
        if (ch != ',')      // 逗號分隔輸入資料,不是逗號就是有效資料
        {
            str[i] = ch;    // 儲存兩個逗號之間的所有數字字元
            i++;
        }
        else                // 這時出現逗號,表示一個有效數字輸入完畢
        {
            i = 0;          // 清零
            numbers.data[j++] = atoi(str);  // 將一個有效數字字元轉成數字,並存入陣列
            numbers.size++;                 // 儲存陣列數量加一
            memset(str, 0, sizeof(str));    // 清空暫存陣列
        }
    }
    if (i != 0)             // 最後一個數組後面可能沒有逗號
    {
        numbers.data[j] = atoi(str);            
        numbers.size++;
    }

    // print_data(numbers);     // 測試列印函式,測試輸入資料是否正確
    // printf("is_sorted() : %d.\n", is_sorted(numbers));     // 測試是否正確排序函式
    fclose(fp);
}

// @brief 排序執行緒的初始化和工作過程
void handle_sorting()
{
    pthread_t ntid1, ntid2;
    parameters * param[2];
    int err1, err2;
    int len = numbers.size / thread_MAX;    // 兩個執行緒的情況下,第二個開始位置
    //parameters * para[2];
    param[0] = (parameters *)malloc(sizeof(parameters));
    param[0]->start = 0;
    param[0]->end = len;
    param[1] = (parameters *)malloc(sizeof(parameters));
    param[1]->start = len + 1;
    param[1]->end = numbers.size - 1;
    // printf("param[0]->start=%d\nparam[0]->end=%d\nparam[1]->start=%d\nparam[1]->end=%d\n",
    //         param[0]->start, param[0]->end, param[1]->start, param[1]->end);
    
    /*  如何用for迴圈,建立任意多個執行緒處理,
        主要是地址分配,不清楚怎麼任意分配 */
    // for (int i = 0; i < thread_MAX ; i++)
    // {
    //     err = pthread_create(&ntid, NULL, sorter, (void *)i);   // 建立新執行緒
    //     if(err != 0){ 
    //         fprintf(stderr, "error create:%s\n", strerror(err)); 
    //         exit(1); 
    //     } 
    // }

    err1 = pthread_create(&ntid1, NULL, sorter, (void *)param[0]);   // 建立新執行緒
    if(err1 != 0){ 
        fprintf(stderr, "error create:%s\n", strerror(err1)); 
        exit(1); 
    } 

    err2 = pthread_create(&ntid2, NULL, sorter, (void *)param[1]);   // 建立新執行緒
    if(err2 != 0){ 
        fprintf(stderr, "error create:%s\n", strerror(err2)); 
        exit(1); 
    } 
    pthread_join(ntid1, NULL);
    pthread_join(ntid2, NULL);

    free(param[0]);
    free(param[1]);
}

void handle_merge()
{
    pthread_t ntid;
    int err;

    parameters * para = (parameters *)malloc(sizeof(parameters));
    para->start = 0;
    para->end = numbers.size - 1;
    
    err = pthread_create(&ntid, NULL, merger, (void *)para);   // 建立新執行緒
    if(err != 0){ 
        fprintf(stderr, "error create:%s\n", strerror(err)); 
        exit(1); 
    } 
    pthread_join(ntid, NULL);
    //sleep(1);       // 不加延時會排序出錯???
    free(para);     // 還不能放上面???
}

// @brief 顯示排序資料是否正確
void show_data()
{
    int res = is_sorted(numbers);
     
    if (res)
    {
        printf("不正確排序.\n");
        print_data(numbers);
    } 
    else {
        printf("正確排序.\n");
        print_data(numbers);
    }
}

void output_data()
{
    FILE *fp;
    int i = 0;
    char ch;

    fp = fopen("out", "w");
    if (fp == NULL)
    {
        printf("error out.\n");
        exit(1);
    }

    while (i < numbers.size)
    {
        fprintf(fp, "%d,", numbers.data[i]);
        i++;
    }

    fclose(fp);
}

void cleanup()
{
    ;
}