Passing parameters in threads

Pages: 12
So, I've figured out some stuff about it, now the only thing I need to learn next is how to pass arguments to threads. Do you do it with pointers? I want to build a program that is working on 1 process at a the same time, my code is below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <process.h>     // needed for _beginthread()</span>
using namespace std;

void assist(int, int);

int main(int nNumberofArgs,char* pszArgs[])
{
    int begin_num;
    int iterations;
    cout << "Hello, insert beginning number: ";
    cin >> begin_num;
    int result = begin_num;
    cout << endl << "Insert number of iterations: ";
    cin >> iterations;
    if(iterations<=1)
    {
        iterations += 1;
    }
    cout << endl;

    _beginthread(assist,0,(void*)iterations,(void*)result);
    for(int a=1;a <= iterations;a++)
    {
        result += 1;
    }
    cout << result << endl;
    system("PAUSE");
    return 0;
}

void assist(int iterations, int result)
{
    for(int b=1;b <= iterations;b++)
    {
        result += 1;
    }
}


I'm doing a simple addition program that is being worked on by two processes (I know that the result will double due to 2 threads). I just need to know how to pass the above parameters >.<, thanks!
This is not as simple of a task as you'd think. There are lots of nuances with multi-threading. In particular... when one thread is writing to a variable, no other thread can be writing/reading it at the same time, or else you get a race condition and your program can explode.

Worse, due to the way modern processors are pipelined, you can never really be sure when reads/writes are actually happening.

So spawning a 2nd thread to modify something in the 1st thread has lots of potential problems. The 2nd thread cannot write to the var unless the 1st thread is not touching it. The only way to ensure this is with a memory barrier (either by using a mutex, or by making the variable in question atomic).

However... both of those techniques are slow. Memory synchronization breaks the pipeline in order to ensure there are no conflicts -- but breaking the pipeline is very costly computationally. As a result... a multithread solution might actually be slower than a single threaded solution. It all depends on how it's implemented.


So my advice is to rethink your problem and ask yourself if a 2nd thread is really necessary. If it is... then what is the goal of this 2nd thread? A realistic scenario will help in explaining how to do this properly. Your 'assist' example is too simplistic to really show this technique effectively.
Well, I just found out how to use the _beginthread() function but I still don't know what goes in between the () really. I was just messing around, but a realistic scenario would to implement this into a dam's system, two generators could be working at the same time and counting the total energy being produced. So can I use pointers or apply a Sleep(x)?

Sorry, I haven't touched my compiler in a long time
Herm... in particular, two threads can read a variable at the same time but only if that data is guaranteed to be constant during that time.

In this case... a future is often well played. You give the future a variable to do some calculation with, do something else, and then fetch the result as late as possible. If you have to fetch the result immediately, this mechanism doesn't work.

In that case, you might want to look into OpenMP to help execute the logic in parallel.

Edit: Also, profile/benchmark if you really care about performance. Just because something sounds good on paper does *not* mean it improves performance by any means.
Last edited on
The only way to synchronize is to use a memory barrier. IE: a mutex, atomic, etc. Something specifically designed to halt the pipeline to ensure there are no race conditions when accessing memory. This cannot be simulated with Sleep/pointers -- you must use actual synchronization tools.

For the dam scenario, you could do something like this. Note I'm going to use C++11 threads and timing stuff here (instead of _beginthread and Sleep) since they are portable and easier to use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Note I'm using globals here for simplicity in this example.  I do not recommend this

#include <thread>       // for std::thread
#include <functional>   // for std::bind
#include <chrono>       // for std::chrono
#include <atomic>       // for std::atomic
#include <cstdlib>      // for rand/srand -- simpler than <random> for this example
#include <ctime>        // for time  (srand)
#include <iostream>     // for cout

typedef std::atomic<int>        atomicint;      // typedef to make atomics easier

// here is the data we are sharing between threads:
atomicint           powerplant1(0);             // the power output for this plant
atomicint           powerplant2(0);
std::atomic<bool>   runplants(true);            // flag to mark when we want the threads to shut down


// here is a the function that each power plant's thread will execute
void runPowerPlant(atomicint& poweroutput, int timeBetweenUpdates)
{
    while(runplants.load())     // keep looping until 'runplants' is false...
    {                           //   at which point this function (and the thread) will exit
        // wait between 10 and 50 ms
        std::this_thread::sleep_for( std::chrono::milliseconds( timeBetweenUpdates ) );

        // then generate 1 unit of power
        ++poweroutput;
    }
}

// now our main function:
int main()
{
    using std::cout;
    using std::endl;

    srand( unsigned(time(nullptr)) );       // seed random number generator

    // create two power plant threads
    std::thread thread1( std::bind(runPowerPlant, std::ref(powerplant1), rand() % 40 + 10) );
    std::thread thread2( std::bind(runPowerPlant, std::ref(powerplant2), rand() % 40 + 10) );

    // wait a bit... then print out how much power they output
    cout << "Waiting..." << endl;
    std::this_thread::sleep_for( std::chrono::milliseconds(1000) );
    cout <<   "PowerPlant 1 output=" << powerplant1.load();
    cout << "\nPowerPlant 2 output=" << powerplant2.load() << endl;

    
    cout << "\n\nWaiting..." << endl;
    std::this_thread::sleep_for( std::chrono::milliseconds(1000) );
    cout <<   "PowerPlant 1 output=" << powerplant1.load();
    cout << "\nPowerPlant 2 output=" << powerplant2.load() << endl;

    // tell our threads it's ok to shut down now
    runplants.store(false);

    // then wait for them to actually shut down
    thread1.join();
    thread2.join();
}


Key things to note here:

- We have 3 variables that are being accessed by multiple threads. Those variables are: powerplant1, powerplant2, and runplants.
- Since those variables are being accessed by multiple threads... they MUST be guarded. No exceptions. Failure to do this results in very strange things happening, and possibly even periodic program crashes.
- In this example I am guarding them by making them atomic.
- Atomic variables cannot be read/written like most variables. Note I can't just say:
runplants = false;... I have to do runplants.store(false);. Likewise, to print the power plant output, I have to call load:
cout << powerplant1.load();.



I'm happy to answer any questions about the code. It's heavy with the C++11 (the only part of C++11 that I didn't use that I probably could have was <random> -- I opted for rand() to keep the example simple)... so if you are learning from a book/tutorial that is older than 3-4 years, all of this will be very new.


If you want... I can show you how to do this same thing safely with _beginthread and Windows mutexes, but it's a bit trickier (and not portable).
Ahh, I can't run this, I have Code::Blocks 13.12, with the flag "Let g++ follow the C++11 ISO C++ language standard [-std=c++11]" ticked on but it's throwing errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Cleaned "Power Plant Threading - Debug"

-------------- Build: Debug in Power Plant Threading (compiler: GNU GCC Compiler)---------------

mingw32-g++.exe -Wall -fexceptions -g -std=c++11  -c "C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp" -o obj\Debug\main.o
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp: In function 'void runPowerPlant(atomicint&, int)':
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:25:14: error: 'std::this_thread' has not been declared
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp: In function 'int main()':
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:41:5: error: 'thread' is not a member of 'std'
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:41:17: error: expected ';' before 'thread1'
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:42:5: error: 'thread' is not a member of 'std'
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:42:17: error: expected ';' before 'thread2'
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:46:10: error: 'std::this_thread' has not been declared
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:52:10: error: 'std::this_thread' has not been declared
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:60:5: error: 'thread1' was not declared in this scope
C:\Users\cocon_000\Desktop\SFML Projects\Power Plant Threading\main.cpp:61:5: error: 'thread2' was not declared in this scope
Process terminated with status 1 (0 minute(s), 0 second(s))
9 error(s), 0 warning(s) (0 minute(s), 0 second(s))
 


strangely, it seems to complain about thread, but not about atomic.

Did you leave out the #include <thread> line?

EDIT:

nope... looks like an issue with the C::B distribution:

http://forums.codeblocks.org/index.php?topic=17838.0;prev_next=prev

C::B comes bundled with a version of gcc that does not include std::thread. Looks like you'd have to download/install a different version of gcc.


EDIT:

Or you could use _beginthread to create the thread rather than std::thread. But then it might be a bit more work to pass the powerplant object.
Last edited on
I've really only done multithreading using the C++ standard library, so this example may not be entirely correct, but maybe it will help you get the idea. It's not tested, but it's supposed to sum up the primes between 1 and lim.

Let each thread work on a partition of the problem and when they all finish, combine the results.

With C++ standard threads, you don't have to pass as void pointer, but for windows threads you do. It's convenient to make a struct and pass a pointer to it. First you have to convert it to a void pointer to pass to the function, then in the function you cast back to the type of pointer it really is.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
struct PrimeSumArgs {

    int sum;
    int start;
    int end;
};

bool isPrime( int n ) {

    int sqrtn = std::sqrt( n );

    if ( n == 2 ) 
        return true;

    for ( int i = 2; i <= sqrtn; ++i ) {
        if ( n % i == 0 )
            return false;
    }
    
    return true;
}

void primeSum( void* _args ) {
    
    PrimeSumArgs *args = ( PrimeSumArgs * ) _args;
    
    for( int i = args->start; i <= args->end; ++i ) {
        if ( isPrime( i ) )
            args->sum += i;
    }
}

int main() {
    
    int lim;
    std::cout << "how far out?\n";
    cin >> lim;

    int workSize = lim/4;

    PrimeSumArgs args1 = { 0, 2, workSize - 1 };
    PrimeSumArgs args2 = { 0, workSize, workSize*2 - 1 };
    PrimeSumArgs args3 = { 0, workSize*2, workSize*3 - 1 };
    PrimeSumArgs args4 = { 0, workSize*3, lim };

    unsigned long handles[4];

    handles[0] = _beginthread( primeSum, 0, (void*) &args1 );
    handles[1] = _beginthread( primeSum, 0, (void*) &args2 );
    handles[2] = _beginthread( primeSum, 0, (void*) &args3 );
    handles[3] = _beginthread( primeSum, 0, (void*) &args4 );

    WaitForMultipleObjects( 
        4,         
        handles,    
        TRUE,    // wait for all of them
        INFINITE // wait until they have finished regardless of how long they take 
    );  
    
    std::cout << "prime sum from between 1 and " << lim << " is " 
        << args1.sum + args2.sum + args3.sum + args4.sum 
        << std:endl;
     
}

//or generalized with respect to thread number

int main() {
   
    const int NUM_THREADS = 16;
 
    int lim;
    std::cout << "how far out?\n";
    cin >> lim;

    int workSize = lim/NUM_THREADS;

    PrimeSumArgs args[ NUM_THREADS ];

    for ( int i = 0; i < NUM_THREADS; ++i ) {
        args[i].sum = 0;
        args[i].start = i*workSize;
        args[i].end = ( i == NUM_THREADS - 1 ) ? lim : (  i + 1 ) * workSize - 1; 
    }

    unsigned long handles[ NUM_THREADS ];

    for ( int i = 0; i < NUM_THREADS; ++i ) 
        handles[i] = _beginthread( primeSum, 0, (void*) &args[i] );

    WaitForMultipleObjects( 
        NUM_THREADS,         
        handles,    
        TRUE,    // wait for all of them
        INFINITE // wait until they have finished regardless of how long they take 
    );  
    
    int sum = 0;
    for ( int i = 0; i < NUM_THREADS; ++i )
        sum += args[i].sum;

    std::cout << "prime sum from between 1 and " << lim << " is " << sum 
        << std::endl;
}


accessed by multiple threads... they MUST be guarded.

The exception is when you are only reading ( not writing ).
Last edited on
The exception is when you are only reading ( not writing ).


Correct. Read-only access is fine.

Though even this you have to be careful with when dealing with complex classes. Poorly written classes may seem like they're doing a read-only op when they are actually modifying the internal state.
Hey Disch, thanks for the EDIT part but I don't fully understand how _beginthread() works, can you explain the parameters that go into the function?
One problem is that you are converting the value of an integer to a void pointer instead of the pointer to the integer to to a void pointer.

I made the same mistake on accident in my example even though I know better (fixed).

You should learn about pointers and structs before tackling threads.
Last edited on
All right, I can figure the pointers and structs out, but a small question, what goes in between the () for _beginthread()? I know the first parameter is function name... etc. Thanks

edit: btw,...

1
2
3
4
5
6
7
8

mingw32-g++.exe -Wall -fexceptions -g -std=c++11  -c "C:\Users\cocon_000\Desktop\SFML Projects\PrimeArgsThreading\main.cpp" -o obj\Debug\main.o
C:\Users\cocon_000\Desktop\SFML Projects\PrimeArgsThreading\main.cpp:8:14: error: 'n' was not declared in this scope
C:\Users\cocon_000\Desktop\SFML Projects\PrimeArgsThreading\main.cpp:9:1: error: expected ',' or ';' before '{' token
Process terminated with status 1 (0 minute(s), 1 second(s))
2 error(s), 0 warning(s) (0 minute(s), 1 second(s))
 


htirwin, this is what I get when I run your program, can you include the libraries you used?
Last edited on
htirwin, this is what I get when I run your program

The error messages tell you where the errors are. On line 8, I left out the type of the parameter. Should be int n ( fixed ). I've been doing a lot of javascript programming lately.

I also had another error on line 25 and more errors in the print statements, and lines 80 to 83. Sorry about that. I was rushing too much. I corrected it.


_beginthread()? I know the first parameter is function name... etc. Thanks


Here is the signature.

1
2
3
4
5
uintptr_t _beginthread( 
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);


The first parameter denotes a function pointer which is essentially what the function name stands for behind the curtains. It is a pointer to a function that takes a void pointer as an argument and returns nothing ( hence void ). This is why your assist function must have the signature void assist( void * arg_name )
http://www.learncpp.com/cpp-tutorial/78-function-pointers/

The second is the stack size for the thread.
http://stackoverflow.com/questions/7538008/what-does-beginthreads-second-argument-stack-size-mean

The third parameter is a void pointer
http://www.learncpp.com/cpp-tutorial/613-void-pointers/.

For simplicity, you can think of variables as mailboxes. They have addresses and contents. The address is the pointer, the contents is the value.

You declare pointers with a type to give the compiler some information about what is actually at that address ( the type of the value stored in that address ). This is important, because when you retrieve the value starting at the address, you need to know how much to take and how to interpret it.

When you cast to a void pointer, you strip away the type information and it becomes just the address without any information about the type of value it points to. In C, it is common in functions such as _beginthread to make the parameter a void pointer so that you can use the same function and still pass any kind of data.

It's important that in the function, you give the pointer it's original type back by casting.

Pointer syntax is what confuses a lot of people.
http://www.cplusplus.com/doc/tutorial/pointers/

In my example, I use a struct to let me group the variables in need to pass to the function into one variable. If you have a variable that represents a struct or class by value, then you access its member variables like this. some_struct.start if you have a variables which is a pointer to a class or struct, you access it's variables like this, some_struct->start

If you have a variable representing the value of something, you get the address where that value is in memory like this, &some_var.

(void*) &some_struct, means cast the address of the value held by some_struct to a typeless pointer.

Then in the function, when you use _arg, you have to make sure it is casted to the correct type. You can just put the cast everywhere it is used, or make a new variable of the correct pointer type and set it equal to the void pointer casted to the correct pointer type.

1
2
3
4
void( void * _arg ) {
    Some_Struct *arg = (Some_Struct*) _arg;
   ...
}

Last edited on
Just a question, what library do you need to include for WaitForMultipleObjects()?

Well, thanks for all of this, I suppose I'll start working on pointers and structs.
Just a question, what library do you need to include for WaitForMultipleObjects()?

You gotta get used to finding this type of information on your own. Just search for it and scroll down till you find the information you need.

On another note. I recommend not wasting your time Windows threads. I would upgrade to a more recent version of your compiler and use C++ standard threads.

Here is one option.

http://sourceforge.net/projects/mingwbuilds/
Last edited on
htirwin wrote:
use C++ standard threads.


Thanks for the advice, so, what exactly does mingwbuilds do? (sorry for that question but my dad needs to know "everything")

So I'm guessing it adds more files to my computer so I can run C++ standard threads?
It's just a compiler that supports C++11 threads (with an environment to run that compiler in).
Last edited on
So, I've installed this as: C:\Program Files\mingw-builds\x64-4.8.1-posix-seh-rev5

Is that right? or do I install all the files into the same directory as my C::B folder?

I see a .bat, and that's the closest I can come to a GUI

how do you use it?
Last edited on
Open up code::blocks and under settings, choose compiler.

Then click copy. Give it a name like mingw-gcc-x64-4.8.1-posix-r5.

Then select the toolchain executables tab. From there you can get an idea what to do from this screenshot.

http://imgur.com/zu5Au3q

After that you can set it to the default if you like. Then press ok.

To choose which compiler you want to use for a given project, choose build options under the project menu. You should see the compiler you added there in a list.
Last edited on
Ahh, it throws the same error as before, I have every setting like the picture you've shown me but it still doesn't work. I have windows, should I have chosen

Threads: win32 instead of posix?
Pages: 12