Solved

How to share variables between threads ?

Posted on 2004-04-19
30
726 Views
Last Modified: 2011-09-20
I have a program using threads.

I have a class:
*************
class test
{
   private:
      int i;
   public:
      void * testing (void *);
}
*************
I want my threads to get the vaue of "i" as well as to be able to write to "i".

void * test::testing(void *)
{
   // I want this fuction to write to "i" by incrementing the vaues of "i"
}

In the futire other threads should also be able to write to "i".

HOW ???
0
Comment
Question by:probine
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 16
  • 6
  • 2
  • +3
30 Comments
 
LVL 30

Expert Comment

by:Axter
ID: 10858256
You can make i static, which would then give you only one instance of i

class test
{
  private:
    static int i;
0
 
LVL 12

Expert Comment

by:stefan73
ID: 10858288
Hi probine,
> In the futire other threads should also be able to write to "i".

As all the threads share the same memory, you can access i. Just make sure you wrap all update operations in a mutex, otherwise you could have strange "lost update" results.

Cheers,
Stefan
0
 

Author Comment

by:probine
ID: 10858575
Axter making "i" static gave me errors, either by having it as private as well as public.

stefan73 you said that all threads share the same memory. Why then when I write to "i" from a thread and read "i" from another, then I see that the result is like if no one have ver written to "i" ???

More suggestions...
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 3

Expert Comment

by:tosch
ID: 10858826
Basically threads are not a language feature of C++ and therefore it has nothing to do with multithreading if you are in the right scope to access a member variable.

If two threads know one and the same object of the class "test" (your example) "i" can be written to from both of them - but only inside member methods of class test (because it is declared private).  

Of course you can use a global structure, global variables or  static fields to make both threads able to know the same object or variable.

Is´s a general problem of mutithreaded programming to be aware of race conditions:

Consider the case of one thread "delete"ing an object at a pointer and another thread working with this very pointer at the same time.

Another example is one thread reads from a shared variable makes some computations and writes back the new value. Another thread starts reading just after the first one and writes back after it and overwrites the result of the first calculation.

This can happen even is you access this variable only in one line of code (because the compiler may decide to put the variable to a CPU register and write it back later).
Bugs of this kind are hard to track and thus it´s good practice to declare such variables as "volatile" - this tells the compiler to always work on memory directly instead of using a CPU register.
So you would write a "volatile" type modifier before the type ("int" in your example).

This is by far not all which a serious programmer has to take care of when having multiple threads dealing with the same memory because you cannot know when exactly the scheduler of the underlying operating system will decide to context switch between the threads.
Operating systems capable of multithreading offer functions to synchronize threads. such as
declaring sections of code as "no more than one thread simultanously" or "not interruptable".  

// test.hpp

#ifndef __test_hpp__
#define __test_hpp__

// our class
class test
{
   private:
      volatile int i;  // i is volatile and shall be kept in memory
   public:

      // I modified your example a little - so testing takes no argument and has no result (void)
      //  instead of a "pointer to arbitrary (void*) " argument and result.

      void testing (void);
};

// there is a aggregate variable of the class "test" called "my_test" somewhere else (in the cpp file)
// to link to
extern test my_test;  

#endif

// cpp file

// my_test definition here
test my_test ( /* constructor arguments go here */);

void test::testing(void)
{
   i += 1;
}


Now you can say "my_test.testing()" in any of your threads causing the member of the object "my_test" of class "test" to be incremented by one.
As mentioned earlier this might not be 100% bulletproof since no mutual exclusion is done here (especially when the expression changing "i" becomes more complex).
Further I use an unscoped global here for the sake of simplicity - putting this inside a namespace or using a static field (if sematically connected to a class) is better style, though.

0
 
LVL 12

Expert Comment

by:stefan73
ID: 10858909
tosch,
> So you would write a "volatile" type modifier before the type ("int"
> in your example).

"volatile" does not guarantee that exactly one thread is using your variable!

If you have a write transaction on your shared variable (that includes increasing it), you need to protect it, for both read and write access.

Stefan
0
 

Author Comment

by:probine
ID: 10859047
Using "volatile int i" gives me an error message saying that "i" is private.

This should be simple... I do not care about the other thread writing to "i" at the same time or anything like it... I just want one thread writing to it, then an other thread writes again...

Do not thing about mutexes, permissions, or anything like it... keep it simple.

HOOOOOOWWWWW????
0
 
LVL 30

Expert Comment

by:Axter
ID: 10859214
Please post exactly what errors you're getting.
0
 
LVL 30

Expert Comment

by:Axter
ID: 10859238
Keep in mind if you make the variable static, you only have one instance of this variable, and it must be defined in your *.cpp file.

Example:
///Your *.h file:
class test
{
  private:
    static int i;


// Your *.cpp file
test::i = 0;//Initialize it to appropriate value
0
 
LVL 30

Expert Comment

by:Axter
ID: 10859285
Correction:
Sorry, but I forgot to put the type for the static variable

// Your *.cpp file
int test::i = 0;
0
 
LVL 30

Expert Comment

by:Axter
ID: 10859307
Making it static and volatile is a good idea as well.
Example:
///Your *.h file:
class test
{
private:
    static int volatile i;
public:
      void * testing (void *);
};

// Your *.cpp file
int volatile test::i = 0;

void * test::testing(void *)
{
   ++i;
   return NULL;
}
0
 
LVL 30

Expert Comment

by:Axter
ID: 10859383
>>"volatile" does not guarantee that exactly one thread is using your variable!

No, but it is a good idea for multithread variable access.

>>If you have a write transaction on your shared variable (that includes increasing it), you need to protect it, for both read
>>and write access.

This depends on the scope of the threads, and the variable type.
For a single integer increment and decrement, a mutex may not be required.
0
 
LVL 2

Expert Comment

by:guntherothk
ID: 10860672
You have several problems here.

First is access to your variable. You need to make your member variable public if you want code outside the class to access it. This by the way has nothing to do with threading. Private means only code in class member functions or friends can access the variable, and public means anybody can access the variable.

Static means all class instances share a single common variable. If the variable is not static, each class instance has its own copy. If the variable is not static, and you create one instance of the class in each thread, you will see only one thread change the value of the variable. This again has less to do with threading than with understanding who is accessing what block of storage.

Volatile means the value of the variable might change by surprise. Normally the compiler pretty well knows when the value of a variable can change. The compiler knows when code reads and writes the variable. The compiler can hold a temporary copy of the variable in a register because it knows its value isn't changing. When you declare a variable volatile, you're telling the compiler you know something the compiler doesn't. In this case you're slaying another thread may make surprise changes in the variable. When a variable is declared volatile, the compiler doesn't try to optimize use of the variable. You want to declare any variable volatile that is fiddled with by more than one thread.

Finally we get to the threading part. If you have static volatile int i, and we have two threads each executing the statement ++i;, we still have a problem. The expression ++i means "load the value of i, add 1 to it, and then store the result in i." That's three steps. Times 2 threads. Suppose i = 0, and the steps happen in the following order
thread 1: load i (get a zero)
thread 1: add 1 to that value (results in a 1)
context switch
thread 2: load i (get a zero because the 1 from thread 1 hasn't been stored yet)
thread 2: add 1 to that value (results in a 1)
thread 2: store the result (stores a 1 in i)
context switch
thread 1: store the value (1) in i !!! You've incremented i twice, but overwritten the result of the first increment.

To solve this problem you need to serialize access to i, which you do with a signal, semaphore, or mutex. You choose. Or you make use of an assembly language instruction that guarantees atomic read/modify/write access. The windows InterlockedIncrement() method encloses such an assembly language statement.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 10860846
If you want to keep it simple and have no fear that two threads try to increment the number simultanously (what might go wrong) take this

// test.h
class test
{
  private:
    static int sharedNum;
  public:
    static int incrementSharedNum()  { return ++sharedNum; }
    static int getSharedNum() { return sharedNum; }
};

// test.cpp
   int test::i = 0;  

// thread.cpp

     int i = test::incrementSharedNum();  // get the incremented number

You may use InterlockedIncrement() on Windows Systems as Gunther has suggested if you want go for sure.

Regards, Alex


0
 
LVL 30

Expert Comment

by:Axter
ID: 10860868
>>The expression ++i means "load the value of i, add 1 to it, and then store the result in i."

That's not correct.  The expresion ++i for an integer means increment i.
In modern computers, this is ONE assembly instruction.

If on the other hand you have "i++", then you could have the above senerio.
This is why Advanced C++ books recomend using prefix increment/decrement over postfix.
Because prefix does not require temporary loading of the variable.
Action can be taken to the variable directly in one machine instruction.

However, postfix increment/decrement requires variable loading.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 10860869
Sorry, replaced i by sharedNum and forgot one statement

// test.cpp
   int test::sharedNum = 0;  

Regards, Alex
0
 
LVL 30

Expert Comment

by:Axter
ID: 10860924
Correction:
>>However, postfix increment/decrement requires variable loading.

However, postfix increment/decrement requires variable loading *if* the compiler does not optimize away the difference.
Most modern compilers will optimize away the difference between postfix and prefix if a built-in type is used, and the postfix logic is not required.
0
 

Author Comment

by:probine
ID: 10861818
Here is my very simple code. I have 2 files. One is test.h and the other one is tes.cpp.

***********************************************************************
// test.h ---------------------------
#include <iostream>
using namespace std;

class test
{
   private:
      int number_probes;      
   public:
      static void * receive (void *);
};

void * test::receive(void *)
{
   test t;
   t.number_probes = t.number_probes + 5 ;
   cout << "Number Probes = " << t.number_probes << "\n";
}
**************************************************************************
// test.cpp

#include <pthread.h>
#include <sstream>
#include <iostream>
#include "test.h"
using namespace std;


int main(int argc, char *argv[])
{
   pthread_t t_receive; // create a thread variable
   test call; // object of the class test.h
   pthread_create(&t_receive, NULL, call.receive, NULL);
   pthread_create(&t_receive, NULL, call.receive, NULL);
}
**************************************************************************

I call twice the pthread... it should increment the value by 5 each time I call the function, but it does not.

Help pease !!!!!!!
0
 
LVL 30

Expert Comment

by:Axter
ID: 10862052
>>Help pease !!!!!!!
I posted a working example.
Did you try it?
0
 
LVL 30

Accepted Solution

by:
Axter earned 50 total points
ID: 10862093
***********************************************************************
// test.h ---------------------------
#include <iostream>
using namespace std;

class test
{
   private:
       static int volatile number_probes;      
   public:
      static void * receive (void *);
};

//**** Important ****
// You need to put both number_probes and the receive function inside the *.cpp file, and NOT in the *.h header
//Test.cpp

#include <pthread.h>
#include <sstream>
#include <iostream>
#include "test.h"
using namespace std;


int volatile test::number_probes = 0;
void * test::receive(void *)
{
   test t;
   t.number_probes = t.number_probes + 5 ;
   cout << "Number Probes = " << t.number_probes << "\n";
}

int main(int argc, char *argv[])
{
   pthread_t t_receive; // create a thread variable
   test call; // object of the class test.h
   pthread_create(&t_receive, NULL, call.receive, NULL);
   pthread_create(&t_receive, NULL, call.receive, NULL);
}
0
 

Author Comment

by:probine
ID: 10862164
Hi  Axter and thank you for your help.

Actually I always knew that I could just have the variables in my .cpp and it will work, but I thought that having them in the .h file could fo the job as well and much more organized...

I thing it might not be possible to have the variables in the class and the functions implemented in the .h file.

mmm... there should be some way to do so.
0
 
LVL 30

Expert Comment

by:Axter
ID: 10862165
FYI:
Your test::receive function is not returning any values.
You need to add a return to the function for it to compile.

Also, since the receive function is static, you don't acctually need an instance of test.

0
 
LVL 30

Expert Comment

by:Axter
ID: 10862199
>>Actually I always knew that I could just have the variables in my .cpp and it will work, but I thought that having them in
>>the .h file could fo the job as well and much more organized...

A static class member variable needs to be declared in the *.cpp file.
0
 

Author Comment

by:probine
ID: 10862246
I really appriciate your help... thank you very much.

Regards,

Probine.
0
 

Author Comment

by:probine
ID: 10862252
One more thing...

Isn't possible to have the variable then as no static, I mean with out declaring it static ???
0
 
LVL 30

Expert Comment

by:Axter
ID: 10862268
>>mmm... there should be some way to do so.
Since your receive function is a static member variable, you really need a static variable for your code.
Which means you need to declare the variable in a *.cpp file.
Where ever you put the function recieve() at, you can put the static variable.

Why don't you want this in the *.cpp file?

FYI:
>>and much more organized...

This would not be organized.  It would be confusing for any C++ developer trying to maintain the code.
The *.h files have a specfic job to do.  And the *.cpp files have their job to do.
Trying to mix the code all in one *.h file, is not a good idea.
It may look easy to code, but it's harder to debug, it takes longer to compiler, and when your code gets too complex, it will be harder to resolve circular references.
0
 
LVL 30

Expert Comment

by:Axter
ID: 10862289
>>Isn't possible to have the variable then as no static, I mean with out declaring it static ???
Yes it is.
But then your static receive() member function will not have access to it.
You'll then have to make an instance of your class, and some how get your receive() function access to the instance.

Furthermore, each thread will need to have access to the instance.
Which means you'll need something like a global instance of the class test.
0
 
LVL 30

Expert Comment

by:Axter
ID: 10862305
FYI:
If you create a global instance of your class test, it will also need to go in the *.cpp file.

//In test.cpp
test volatile MyGlobalVar;
0
 
LVL 3

Expert Comment

by:tosch
ID: 10864119
> > So you would write a "volatile" type modifier before the type ("int"
> > in your example).
>
> "volatile" does not guarantee that exactly one thread is using your variable!

Volatile guarantees that the variable refers to memory. That's all I said.
I slightly remember to have mentioned that there are additional steps needed for guaranteed behaviour...
Anyway, volatile is as far as you can go on C++ language level, the additional steps depend on the multithreading API you are using.

Did you read my post at all ?!

[cite from my previous post]
Another example is one thread reads from a shared variable makes some computations and writes back the new value. Another thread starts reading just after the first one and writes back after it and overwrites the result of the first calculation.
[...]
This can happen even is you access this variable only in one line of code

This is by far not all which a serious programmer has to take care of when having multiple threads dealing with the same memory because you cannot know when exactly the scheduler of the underlying operating system will decide to context switch between the threads.
[/cite]
0
 
LVL 2

Expert Comment

by:guntherothk
ID: 10864377
Since we're all flogging this dead horse...

Axter thinks there is a difference between ++i and i++, and that ++i is guaranteed to generate a single instruction. Both those results depend on the compiler and the hardware. Fundamentally, ++i is a read-modify-write that makes two accesses to memory. Lots of modern processors have an increment instruction, but you have to know how the memory interface works to know if that instruction guarantees atomicity or not. The memory scheduler can choose to perform the read and write atomically or not, so it's best to be suspicious of the atomicity of a construct at the programming language level unless you are VERY VERY sure.

On Intel uniprocessor PCs, there is an increment instruction, and it is atomic. Once you add in hyperthreading, I'm not sure.
0
 
LVL 30

Expert Comment

by:Axter
ID: 10865868
>>Axter thinks there is a difference between ++i and i++,
I don't think this.  It's a fact.
However, as I already stated, most modern compilers will optimize away the difference if you're using a built-in type.

See "More Effective C++" by Scott Meyers.
Item 6. "Distinguish between prefix and postfix forms of increment and decrement operators."

Also see the C++ standard.
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

733 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question