Solved

Visual C++ with Boost::thread

Posted on 2013-06-04
17
542 Views
Last Modified: 2013-06-19
Dear experts,

I am writing a program that uses boost::thread. Each thread performs a task that is idenependent from the other threads. This means, each thread calls a member function of an object that is only used by this thread, such that there are no variables used by multiple threads.

Nevertheless, I have the problem that the program works fine as long as only one thread is used. As soon as more than one thread is used the program never ends. When I look into the Windows task manager it still produces CPU-load but memory usage does not grow up.

The relevant code is:

vector<boost::thread *> *workerThreads = new vector<boost::thread *>;
workerThreads->clear( );

for( int i=0; i < numberOfCores; i++ )
{
	workerThread = new boost::thread( &Anova::monteCarlo, anovas->at( i ) );
  	workerThreads->push_back( workerThread );
}

for( int i=0; i < numberOfCores; i++ )
		workerThreads->at( i )->join( );

Open in new window


where anovas is a vector of pointers to objects of type Anova and montoCarlo is a member function of Anova.

Thanks in advance,

Albert
0
Comment
Question by:Albert-Georg
  • 7
  • 5
  • 3
  • +1
17 Comments
 
LVL 40

Expert Comment

by:evilrix
ID: 39221948
So, each thread has its own instance of Anova, each being an instance pulled from the vector?

What does Anova::monteCarlo do? It's possible you have a dead-lock in that method but without seeing it it's hard to say.
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39222018
Yes, each thread has its own instance of Anova.

I have seen now, that it works in debug mode. Maybe, there is a problem with the libraries.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 39222024
Again: What does Anova::monteCarlo do? Without seeing this code the best I can do is speculate.

FWIW, there is unlikely to be anything wrong with boost::thread. I use it all the time without such issues.
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39222050
Anova::monteCarlo does a set of matrix operations. The complete code would be too complicated to post here. It only calls methods and variables of Anova.
0
 
LVL 30

Expert Comment

by:Zoppo
ID: 39222104
Hi Albert-Georg,

I would anyhow suggest you post the code of Anova::monteCarlo here, otherwise IMO there's no real way to help you.

As evilrix mentioned it's hard to believe the problem is within boost::thread, so most probably the problem comes from Anova::monteCarlo.

You wrote it works in DEBUG but doesn't work in RELEASE, so a possible reason could be an uninitialized numeric variable - usually uninitialized numeric variables are set to 0 in DEBUG builds but not in RELEASE builds, i.o.w. uninitialized variables can have any arbitrary, unpredictable value in RELEASE builds.

ZOPPO
0
 
LVL 40

Expert Comment

by:evilrix
ID: 39222130
>> I have seen now, that it works in debug mode.
Are you using a global variable to signal the theads to stop? May be a bool or something like that? If so and if it isn't declared as volatile it is almost certainly optimized out in the threads that are reading it.

http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766

Better still, you should be using either a boost::condition_variable or a std::atomic (if you are using c++11) variable.

Again, without seeing the code this is just conjecture (but the most likely candidate given the symptoms you've described).
0
 
LVL 32

Accepted Solution

by:
phoffric earned 500 total points
ID: 39223193
Changing modes from Release to Debug (or vice-versa) can affect race conditions as well as introduce memory corruption issues.

Try using one of these Windows tools to find the problem. These windows tools allow a free evaluation period. If one of them helps you, you should purchase it for future work.

IBM Rational Purify

Parasoft® Insure++ Runtime Analysis and Memory Error Detection for C and C++

Intel® Inspector XE 2013

Alternatively, if the rest of your program is portable, then you can run it on a Linux box for testing with the free product:

Helgrind: a thread error detector

which is part of the Valgrind suite of error checking tools.
0
 
LVL 32

Expert Comment

by:phoffric
ID: 39223197
I have used the Parasoft Insure++ product on legacy code that started to fail after an OS upgrade with great success.
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 1

Author Comment

by:Albert-Georg
ID: 39223303
Dear experts,

thank you very much for the valuable tips. I do not use a global variable in order to stop the threads. Anova::monteCarlo contains a for-loop with a fixed number of loop passes.

I will try one of the thread-debugging tools phoffric recommended.

Sincerely,

Albert
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39228359
I have done further debugging and have found the critical code. Anova::monteCarlo calls a method for computing the pseudoinverse of a matrix. In order to do so a (C)Lapack routine is called. Since Clapack is not C++ I think this could be the problem with multiple threads.

The relevant code is:

void LinAlg::pseudoInverse( double* A, double* Apinv, integer rowsA, integer columnsA )
{
	// A is m * n
	// Apinv is n * m
	integer m = rowsA;
	integer n = columnsA;
	
	// Singular value decomposition of Matrix A
	double *s = new double[min( m, n )];
	double *U = new double[m*m];
	double *Vt = new double[n*n];
	double *Temp = new double[n*m];

	svd( A, s, U, Vt, m, n, true );

	
	// Computing the pseudo-inverse of S (which is a diagonal matrix containing the singular values of s): Spinv (is n x m)
	double *Spinv = new double[n*m];
	int svIndex = 0; // index of vector s

	for( int i=0; i < (n*m); i++ )
	{
		if( i % (n+1) == 0 ) // main diagonal
		{
			if( fabs( s[svIndex] ) > ZERO_TOLERANCE ) // non-zero values of s are inverted
				Spinv[i] = 1.0f / s[svIndex];
			else // zero-values of s
				Spinv[i] = 0.0f;
			svIndex++;
		}
		else // off main diagonal
			Spinv[i] = 0.0f;
	}


	// Compute Apinv = V * Spinv * U'
	multiply( Vt, Spinv, Temp, n, n, n, m, true, false ); // transpose Vt
	multiply( Temp, U, Apinv, n, m, m, m, false, true ); // transpose U; Temp is n x m


	SAFE_DELETE_ARRAY( s );
	SAFE_DELETE_ARRAY( U );
	SAFE_DELETE_ARRAY( Vt );
	SAFE_DELETE_ARRAY( Spinv );
	SAFE_DELETE_ARRAY( Temp );
}

Open in new window


As soon as array s is deleted an error occurs in free.c in the function HeapFree.

So my question is: May classicak C-code (like the one of Clapack) called by multiple threads of Anova::monteCarlo and if not, what would be a thread-safe way to rewrite it?

Thanks,

Albert
0
 
LVL 40

Expert Comment

by:evilrix
ID: 39228380
>> Since Clapack is not C++ I think this could be the problem with multiple threads.
Unlikely to make any difference since C++ (at least up until the C++11 version) has no specific knowledge about threads. Even if it did (C++11 does), it's still just another thread of execution and providing the code has been written to be thread safe (no race conditions and so on) it should just work.

>> May classicak C-code (like the one of Clapack) called by multiple threads
Yes.

>> As soon as array s is deleted an error occurs in free.c in the function HeapFree.
Are you using a build that is using a C/C++ runtime that is has multi-threaded support? The standard heap allocation and deallocation routines are *NOT* thread-safe (again, because neither C not C++ [before C++11] know anything about threads]. Unless you are using a MT version of the runtime your could have a race condition when calling heap functions.
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39228386
These are good news. Yes, I use an MT version of the runtime.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 39228428
Hmmm. Then I'm afraid I'm all out of ideas at this point. I'd be tempted to use the debugger to break at that point in the code and to check the state of the variables. I know this only happens in release but as long as you build the release with symbols you can still debug it (although as the code will be optimized you may find the debugger jumps around a little).

MT issues are very hard to diagnose, especially when you can't see all the code and can't look at things in a debugger. To simplify, I'd just spin up two threads (make sure you still see the problem) and then try and diagnose. This will be significantly easier than trying to debug with many threads.

I also note you are passing pointers (function arguments) around that are non-const. I would make these const to ensure the memory they point to isn't being modified without you realising in multiple threads. This will not be possible if these pointers are const.
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39258869
Dear experts,

thank you very much again for your help. I tested my code with Intel Inspector and found out that some of the libraries I used were not thread-save. Variables were not initialized and static variables were used. Now it seems to work correctly.

Sincerely,

Albert
0
 
LVL 32

Expert Comment

by:phoffric
ID: 39259480
>> Now it seems to work correctly.
      If you have the luxury of extra time, try using another tool to see if you can catch other latent errors. Or, at least, keep running the Intel Inspector on a regular basis, since race conditions that may exist may sporadically cause other errors to crop up. The benefit of these tools is their ability to catch run-time errors. But that means that you have to exercise your programs long enough with a variety of inputs to catch more errors. To catch more unusual errors, you can even do Fuzz Testing with the Inspector watching. You should be able to apply the Inspector to different sections of your program at a time if your program starts to run too slowly.
0
 
LVL 1

Author Comment

by:Albert-Georg
ID: 39259512
Thank you for these tips. I bought the inspector in order to be able to do regular testing. It is really a great tool!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
Article by: Nadia
Suppose you use Uber application as a rider and you request a ride to go from one place to another. Your driver just arrived at the parking lot of your place. The only thing you know about the ride is the license plate number. How do you find your U…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
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.

760 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

23 Experts available now in Live!

Get 1:1 Help Now