多线程硬币投掷实验

Non*_*714 -1 c++ multithreading pointers dereference dynamic-allocation

预备要点:我需要做什么改变才能在我的记录中显示正确的头/尾值?

编辑1:一旦有超过1个线程,Record内部的整数数组似乎会填充随机值.

我一直试图调试这几天了.我的代码已完成并完全执行.(众所周知,我是一名学生而不是假装成为一名专业程序员.)

在我的多线程硬币投掷程序中,我试图捕获每个线程中出现头或尾的次数.我正在翻硬币总共100,000,000次.(一亿.)

记录类中数组中的元素没有正确累积.我知道我不需要使用互斥锁,因为每个线程都访问一个独特的线程内存位置.但是线程正在更新错误的值,这不应该发生.

以下是我的调试示例.

  1. 单线程(注意正确的头/尾数量):

单线程的调试输出.

  1. 两个线程(现在头/尾计数只是疯了.):

调试两个线程的输出.

下面是完全可执行的代码示例,然后是示例输出.此外,这里还链接到Github上的完整代码:

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <random>
#include <algorithm>
#include <time.h>
#include <strstream>
#include <random>


using namespace std;

default_random_engine dre;
uniform_int_distribution<int> Tosser(0,1);

struct Record {
    Record();
    ~Record();
    int numThreads;
    int *heads;
    int *tails;
    time_t startTime;
    time_t stopTime;
    time_t duration;
    int timesToToss;
};

Record::Record(): numThreads(0), heads(NULL), tails(NULL), startTime(NULL), stopTime(NULL), timesToToss(0){}
Record::~Record() {
    startTime = NULL;
    stopTime = NULL;
    duration = NULL;
    timesToToss = NULL;
    delete [] heads;
    heads = NULL;
    delete [] tails;
    tails = NULL;
    numThreads = NULL;
}

void concurrency(){
    vector<thread> threads;

    Record *records = new Record[4];
    Record *recPtr;
    int *numThrPtr;
    int *headsPtr;
    int *tailsPtr;
    time_t *startTimePtr;
    time_t *stopTimePtr;

    vector<time_t> durations;

    int timesToToss = 100000000; // Times to flip the coin.
    int index = 0; // Controls which record is being accessed.

    for(int i=1;i<3;i*=2){ //Performs 2 loops. 'i' is calculated to represent the number of threads for each test: 1, and 2 (full code contains up to 8 threads.)

        recPtr = &records[index]; //Get the address of the next record in the Record array.
        recPtr->timesToToss = timesToToss; //
        recPtr->numThreads = i; //Record the quantity of threads.
        recPtr->heads = new int[recPtr->numThreads]; //Create a new heads array, of 'x' elements, determined by number of threads.
        recPtr->tails = new int[recPtr->numThreads]; //Create a new tails array, of 'x' elements, determined by number of threads.
        recPtr->startTime = time(0); //Record the start time.

        for(int j = 0;j<recPtr->numThreads;j++){ //Start multi-threading.

            headsPtr = &recPtr->heads[j]; // Get the address of the index of the array, one element for each thread for heads.
            tailsPtr = &recPtr->tails[j]; // Get the address of the index of the array, one element for each thread for heads.

            threads.push_back(thread([&headsPtr, &tailsPtr, timesToToss](){for(int k=0;k<timesToToss;k++){ if (Tosser(dre)) ++(*headsPtr); else ++(*tailsPtr); } })); //Toss a coin!
        }

        for(auto& thread: threads) thread.join(); // Collect/join all the threads.

        while(!threads.empty()){ //Don't want to try and join 'live' threads with 'dead' ones!
            threads.pop_back();//Clear out the threads array to start with an empty array the next iteration.
        }

        recPtr->stopTime = time(0); //Record the end time.

        recPtr->duration = recPtr->stopTime - recPtr->startTime;

        timesToToss /= 2; //Recalculate timesToToss.
        ++index; //Increase the index.
    }

    for (int i=0;i<4;i++){ //Display the records.
        recPtr = &records[i];
        cout << "\nRecord #" << i+1 << ", " << recPtr->numThreads << " threads.";
        cout << "\nStart time: " << recPtr->startTime;
        cout << "\nStop time: " << recPtr->stopTime;
        cout << "\nTossed " << recPtr->timesToToss << " times (each thread).";
        cout << "\nHeads appeared << " << recPtr->heads << " times.";
        cout << "\nTails appeared << " << recPtr->tails << " times.";
        cout << "\nIt took " << recPtr->duration << " seconds.";
        durations.push_back(recPtr->duration);
        cout << "\n" << endl;
    }

    sort(durations.begin(),durations.end());

    cout << "Shortest duration: " << durations[0] << " seconds." << endl;

    delete [] records;
    records = NULL;
}

int main() {

    concurrency();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

记录#2的输出是[更新时间为5/5/2016 @ 2:16 pm CST]:

Record #2, 2 threads.
Start time: 1462472702
Stop time: 1462472709
Tossed 50000000 times (each thread).
Heads appeared << 474443746 times.
Tails appeared << -1829315114 times.
It took 7 seconds.

Shortest duration: 3 seconds.

Process finished with exit code 0
Run Code Online (Sandbox Code Playgroud)

use*_*301 5

int *heads;定义heads为指针.

默认行为<<是打印指针的地址,而不是指向的数据.char打印出c风格字符串的指针有一个例外.

由于每个线程都有自己int的数来计算线程生成的磁头数,因此当线程完成时,磁头数必须相加并打印总和.

此外,

recPtr->heads = new int[recPtr->numThreads]; 
Run Code Online (Sandbox Code Playgroud)

为头部计数器分配了存储空间,但我在代码中找不到任何内容来初始化它们.这是未定义的行为.一个简单的黑客修复方法是:

    for(int j = 0;j<recPtr->numThreads;j++){

        recPtr->heads[j] = 0;
        recPtr->tails[j] = 0;
        headsPtr = &recPtr->heads[j]; // Get the address of the index of the array, one element for each thread for heads.
        tailsPtr = &recPtr->tails[j]; // Get the address of the index of the array, one element for each thread for heads.

        threads.push_back(thread([&headsPtr, &tailsPtr, timesToToss]()
        {
            for(int k=0;k<timesToToss;k++)
            { 
                if (Tosser(dre)) ++(*headsPtr); 
                else ++(*tailsPtr); 
            } 
        })); //Toss a coin!
    }
Run Code Online (Sandbox Code Playgroud)

最后,(编辑3)拉姆达定义thread([&headsPtr, &tailsPtr, timesToToss]()捕获指针headsPtrtailsPtr,因此由当时的线程得到一个开始的机会,所有的线程都在指向headsPtrtailsPtr最后一个线程.

Hack kludge现在是:

    for (int j = 0; j < recPtr->numThreads; j++)
    {

        recPtr->heads[j] = 0;
        recPtr->tails[j] = 0;
        headsPtr = &recPtr->heads[j]; // Get the address of the index of the array, one element for each thread for heads.
        tailsPtr = &recPtr->tails[j]; // Get the address of the index of the array, one element for each thread for heads.

        threads.push_back(thread([headsPtr, tailsPtr, timesToToss]()
        {
            for(int k=0;k<timesToToss;k++)
            {   
                if (Tosser(dre)) ++(*headsPtr);
                else ++(*tailsPtr);
            }
        })); //Toss a coin!
    }
Run Code Online (Sandbox Code Playgroud)

我已经清理了lambda的格式,以便更容易阅读.