Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems passing array by reference to threads

I'm learning threading and I've found some simple examples.

What I'm hoping to do is create 5 threads, that each assign a random number to an array of 20 int's. Then finally have another 5 threads that reconstruct this array to a larger 100 sized int.

Here's some prior code I was trying. I was hoping to be able to pass an array by reference, with no luck.

Any ideas would be appreciated, please keep in mind, I'm completely new to threads

#include <process.h>
#include <windows.h>
#include <iostream>
#include <fstream>
#include <time.h>
//#include <thread>

using namespace std;

void myThread (void *dummy );
void myThread2 (void *dummy );

int main()
{

    ofstream myfile;
    myfile.open ("coinToss.csv");

    int rNum;

    long numRuns;
    long count = 0;
    int divisor = 1;
    float holder = 0;
    int counter = 0;
    float percent = 0.0;

    int array1[1000000];
    int array2[1000000];


    srand ( time(NULL) );

    printf ("Runs (use multiple of 10)? ");
    cin >> numRuns;

    for (int i = 0; i < numRuns; i++)
    {
        _beginthread( myThread, 0, (void *) (array1) );
        _beginthread( myThread2, 0, (void *) (array2) );

    }

}

void myThread (void *param )
{
    int i = *(int *)param;

    for (int x = 0; x < 1000000; x++)
    {
        //param[x] = rand() % 2 + 1;
        i[x] = rand() % 2 + 1;
    }

}

void myThread2 (void *param )
{
    int i[1000000] = *(int *)param;

    for (int = 0; x < 1000000; x++)
    {
        i[x] = rand() % 2 + 1;
    }

}
like image 823
thistleknot Avatar asked Jan 16 '23 09:01

thistleknot


2 Answers

First thing you need to realize:

 for (int i = 0; i < numRuns; i++)
    {
        _beginthread( myThread, 0, (void *) (array1) );
        _beginthread( myThread2, 0, (void *) (array2) );

    }

The calls to _beginthread return immediately. They don't wait for the threads to finish, or even to start. It just queues up the thread in the OS's scheduler and returns.

However, the code above is the end of the main() function. I very much doubt that under a Release build your threads will even have initialized before your whole program exits. You need to build a mechanism by which your main thread will wait for the worker threads to finish their work before your program shuts down. Doing this is way beyond the scope of an SO post, but look in to CreateEvent() and WaitForMultipleObjects().

Next thing you need to understand is the lifetime and ownership semantics of the stuff you send to the threads. You are passing pointers to arrays which are automatic variables scoped in main():

 int array1[1000000];
 int array2[1000000];

As soon as the scope in which these arrays are declared (here, main()) exits, the variables cease to exist. It is rarely, if ever, correct to pass a pointer to a locally-scoped variable to a worker thread -- never correct if the local scope exits before the thread finishes.

Dynamically allocating those arrays and then transferring ownership of them to the worker threads will fix that problem here. When doing this, please be careful when managing the ownership semantics of these objects/arrays.

like image 147
John Dibling Avatar answered Jan 29 '23 12:01

John Dibling


1. Casting:

Note the casting!

void myThread (void *param )
{
  int *i = (int *) param;
  for (int x = 0; x < 1000000; x++)
  {
    i[x] = rand() % 2 + 1;
  }
}

2. Scope:

Both threads are started numRunstimes. Starting them will take some time. Executing them too. The main should not be ended without respecting the threads.You should monitor the threads by means of WaitForMultipleObjects. _beginthread() returns a waitable handle.

The implementation:

int main()
{
  long numRuns;
  HANDLE hThread[MAX_WAIT_OBJECTS];

  cin >> numRuns;
  // numRuns to be smaller than MAX_WAIT_OBJECTS/2!!!!


  for (int i = 0; i < numRuns; i++)
  {
     hThread[i * 2]     = _beginthread( myThread, 0, (void *) (array1) );
     hThread[i * 2 + 1] = _beginthread( myThread2, 0, (void *) (array2) );
     // or better use _beginthreadex(...)
  }
  WaitForMultipleObjects(numRuns * 2, hThread, TRUE, INFINITE);
  // bWaitAll flag set TRUE causes this statement to wait until all threads have finished.
  // dwMilliseconds set to INFINITE will force the wait to not timeout.
}

This way the main will only finish when all threads have done their job.

3. Access:

Both arrays are declared in the main section, so threads are sharing them. In order to protect access, you shoud introduce some kind of exclusivity. The easiest is a Critical Section Object. When all threads2 are only accessing array2 and all threads1 are only accessing array1, I'd suggest to use 2 critical section objects. A critical section can only be accessed by one thread at a time.

Implementation:

CRITICAL_SECTION cs1,cs2; // global

int main()
{
  long numRuns;
  HANDLE hThread[1000];

  // critical section object need to be initialized before aquired
  InitializeCriticalSection(&cs1);
  InitializeCriticalSection(&cs2);

  cin >> numRuns;

  for (int i = 0; i < numRuns; i++)
  {
    hThread[i * 2]     = _beginthread( myThread, 0, (void *) (array1) );
    hThread[i * 2 + 1] = _beginthread( myThread2, 0, (void *) (array2) );
  }
  WaitForMultipleObjects(numRuns * 2, hThread, TRUE, INFINITE);
}

And the threads (only one shown here):

void myThread1 (void *param )
{
  EnterCriticalSection(&cs1); //aquire the critical section object
  int *i = (int *) param;
  for (int x = 0; x < 1000000; x++)
  {
    i[x] = rand() % 2 + 1;
  }
  LeaveCriticalSection(&cs1); // release the critical section object
}

However: The whole story is yet a bit unclear. You want a number of threads fill the same array from the beginning to the end at the same time. That sounds weird. With the critical section objects as described here, the threads are going to be executed one after eachother anyway. What exactly are you aiming for?

like image 35
Arno Avatar answered Jan 29 '23 10:01

Arno