排序比較器(下)

接着上篇繼續寫道:

三、在子線程中測試排序
一開始編寫代碼的時候,排序過程是在主線程中完成的,這樣的話,一旦數據量級過大(即出現耗時操作),界面就會出現卡死凍結。爲了解決這個問題就使用了Qt的子線程。在子線程中進行排序,也就是使用QObject::moveToThread()這個函數。
對於在子線程中編寫代碼來說,不太向主線程那樣好控制(或者),一般來說,適用於把邏輯簡單的,操作耗時的操作放在子線程中。在本程序中,不僅把排序操作,還把寫入日誌操作放進了子線程。然後主線程和子線程通過信號和槽來進行交流。(主線程釋放工作信號給子線程,子線程接受信號開始工作,工作完之後,釋放工作完成信號,通知主線程。)

主要代碼如下:

//排序工作
void SortWorker::RecSortWorkSig(int i, Sort * sorts)
{

    sorts->InitData();       //拷貝生成測試數據

    //C語言中的函數庫,用作微秒級計時
    struct timeval tpstart,tpend;
    double timeuse;
    gettimeofday(&tpstart,NULL);

    switch (i) {
    case SIM_INS_SORT:
        sorts->SimInsSort();     //測試簡單插入排序
        break;
    case SHELL_SORT:
        sorts->ShellSort();  //測試希爾插入排序
        break;
    case BUB_SORT:
        sorts->BubSort();        //測試冒泡排序
        break;
    case QUI_SORT:
        sorts->QuiSort(0,sorts->GetDataLength()-1);      //測試快速排序
        break;
    case SIM_SEL_SORT:
        sorts->SimSelSort();     //測試簡單排序
        break;
    case HEAP_SORT:
        sorts->HeapSort();       //測試堆排序
        break;
    case MERGE_SORT:
        sorts->MergeSort(0,sorts->GetDataLength()-1);        //測試二路歸併排序
        break;
    case COUNT_SORT:
        sorts->CountSort();      //測試計數排序
        break;
    case RAD_SORT:
        sorts->RadSort();        //測試基數排序
        break;
    default:
        break;
    }

    gettimeofday(&tpend,NULL);
    timeuse=(1000000*(tpend.tv_sec-tpstart.tv_sec) + tpend.tv_usec-tpstart.tv_usec)/1000000.0;
    emit SendSortFinishedSig(timeuse);     //釋放結束信號,傳遞用時參數給主線程
}

//寫入工作
void SortWorker::RecWriLogWorkSig(Sort *sorts,QTextStream *out)
{
    (*out)<<"\nOriginal data:";

    //寫入原始數據
    int length = sorts->GetDataLength();
    for(int i = 0;i<length;++i)
    {
        if(i % 10 == 0)
            (*out)<<"\n"<<sorts->data0[i];
        else
            (*out)<<" "<<sorts->data0[i];
    }
    (*out)<<"\nData after sort:";
    for(int i = 0;i<length;++i)
    {
        if(i % 10 == 0)
            (*out)<<"\n"<<sorts->data[i];
        else
            (*out)<<" "<<sorts->data[i];
    }
    emit SendWriFinishedSig();         //釋放寫入完成信號
}

這裏需要關聯主線程和子線程的信號和槽。 Qt的機制保證這裏主線程和子線程的的信號和槽可以相互關聯。

//關聯信號,用於啓動排序新線程並從新線程中讀取返回的數據
    connect(this,SIGNAL(SendSortWorkSin(int,Sort *)),&sort_worker,SLOT(RecSortWorkSig(int,Sort*)));
 connect(&sort_worker,SIGNAL(SendSortFinishedSig(double)),this,SLOT(RecSortFinishedSig(double)));
    connect(this,SIGNAL(SendWriWorkSig(Sort*,QTextStream *)),&sort_worker,SLOT(RecWriLogWorkSig(Sort*,QTextStream*)));
    connect(&sort_worker,SIGNAL(SendWriFinishedSig()),this,SLOT(RecWriFinishedSig()));

四、等待框的製作
當後臺子線程在進行排序和寫入日誌的過程中,界面不接受用戶的輸入。此處顯示了一個動態的gif等待圖片,阻礙主界面。當子線程工作完成之後,釋放工作完成信號。主程序接受到信號之後關閉等待框,繼續往下執行。如下圖所示:等待圖片
主要代碼如下:

  //顯示等待動畫
     movie = new QMovie(":/img/wait.gif");
     movie->setScaledSize(QSize(ui->label_wait->width(),ui->label_wait->height()));
     ui->label_wait->setMovie(movie);
     movie->start();

五、測試結果
最終的排序結果將會根據用戶的選擇是否記錄在日誌中(比較結果一定會記錄,用戶可以決定選擇是否記錄原始數據和排序數據)。注意:日誌是的目錄是在程序編譯的目錄,不是源代碼的目錄。
以下在我電腦上一個數量級爲10000的排序結果:

選擇的數量規模爲:10000
測試比較開始...

簡單插入排序開始...
簡單插入排序結束。此排序所用時間爲:0.166901s
希爾排序開始...
希爾排序結束。此排序所用時間爲:0.002999s
冒泡排序開始...
冒泡排序結束。此排序所用時間爲:0.48223s
快速排序開始...
快速排序結束。此排序所用時間爲:0.002s
簡單選擇排序開始...
簡單選擇排序結束。此排序所用時間爲:0.192889s
堆排序開始...
堆排序結束。此排序所用時間爲:0.003997s
二路歸併排序開始...
二路歸併排序結束。此排序所用時間爲:0.004997s
計數排序開始...
計數排序結束。此排序所用時間爲:0.012993s
基數排序開始...
基數排序結束。此排序所用時間爲:0.001998s
測試比較結束

本程序最大測試百萬級的數據規模,但是對於複雜度爲O(N2)的算法來說,十分耗時。比如說冒泡排序,我吃飯之前開始測試,吃完之後,等待框還在繼續。但是其他的算法,運行百萬級的數據規模還能在可接受的時間內結束。如下圖所示:
結果顯示
在數據量較小的時候觀察不太明顯,數據量一旦很大,O(N2)和O(logN)明顯就出來了。

完成的工程代碼見:點這兒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章