Solved

How to share variables between threads ?

Posted on 2004-04-19
30
713 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
  • 16
  • 6
  • 2
  • +3
30 Comments
 
LVL 30

Expert Comment

by:Axter
Comment Utility
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
Comment Utility
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
Comment Utility
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
 
LVL 3

Expert Comment

by:tosch
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Please post exactly what errors you're getting.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
>>"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
Comment Utility
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
Comment Utility
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
Comment Utility
>>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
Comment Utility
Sorry, replaced i by sharedNum and forgot one statement

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

Regards, Alex
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 30

Expert Comment

by:Axter
Comment Utility
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
Comment Utility
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
Comment Utility
>>Help pease !!!!!!!
I posted a working example.
Did you try it?
0
 
LVL 30

Accepted Solution

by:
Axter earned 50 total points
Comment Utility
***********************************************************************
// 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
Comment Utility
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
Comment Utility
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
Comment Utility
>>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
Comment Utility
I really appriciate your help... thank you very much.

Regards,

Probine.
0
 

Author Comment

by:probine
Comment Utility
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
Comment Utility
>>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
Comment Utility
>>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
Comment Utility
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
Comment Utility
> > 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
Comment Utility
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
Comment Utility
>>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

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

772 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now