[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 743
  • Last Modified:

How to share variables between threads ?

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
probine
Asked:
probine
  • 16
  • 6
  • 2
  • +3
1 Solution
 
AxterCommented:
You can make i static, which would then give you only one instance of i

class test
{
  private:
    static int i;
0
 
stefan73Commented:
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
 
probineAuthor Commented:
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
toschCommented:
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
 
stefan73Commented:
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
 
probineAuthor Commented:
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
 
AxterCommented:
Please post exactly what errors you're getting.
0
 
AxterCommented:
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
 
AxterCommented:
Correction:
Sorry, but I forgot to put the type for the static variable

// Your *.cpp file
int test::i = 0;
0
 
AxterCommented:
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
 
AxterCommented:
>>"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
 
guntherothkCommented:
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
 
itsmeandnobodyelseCommented:
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
 
AxterCommented:
>>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
 
itsmeandnobodyelseCommented:
Sorry, replaced i by sharedNum and forgot one statement

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

Regards, Alex
0
 
AxterCommented:
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
 
probineAuthor Commented:
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
 
AxterCommented:
>>Help pease !!!!!!!
I posted a working example.
Did you try it?
0
 
AxterCommented:
***********************************************************************
// 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
 
probineAuthor Commented:
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
 
AxterCommented:
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
 
AxterCommented:
>>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
 
probineAuthor Commented:
I really appriciate your help... thank you very much.

Regards,

Probine.
0
 
probineAuthor Commented:
One more thing...

Isn't possible to have the variable then as no static, I mean with out declaring it static ???
0
 
AxterCommented:
>>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
 
AxterCommented:
>>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
 
AxterCommented:
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
 
toschCommented:
> > 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
 
guntherothkCommented:
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
 
AxterCommented:
>>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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 16
  • 6
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now