Copyright © 2006 by Stand__Sure and THA
No programmer in his right mind wants to think about thread
synchronization as he is coding his app. -- Jeffrey Richter
Threading is a necessary "evil" in the world of programming. Users demand applications with graphical user interfaces that remain responsive while "things" are being done in the background, often calls are made to functions or devices that do not return instantly, etc. Writing linear programs that block until such operations are complete does not accomplish these simple design goals. Modern operating systems allow programmers to write multi-threaded code that splits certain sequences of instructions into separate execution chains that either run on separate processors or in time "slices" that are scheduled by the OS for operation based upon some algorithm.
In many simple cases, multi-threaded applications perform exactly as the coder wishes. A simple example of multi-threaded behavior that is generally reliable is when the user clicks a button on a form/window and a small section of code executes in response while the form/window remains responsive (it can be moved, resized, etc.). The user caused our program to do something, yet the GUI remained responsive.
The more complex our applications become, however, the greater the chances that our multi-threading may perform correctly in most but not all cases. Sometimes, our applications may stall; sometimes, they may produce unreliable results (i.e. results that are sporadically wrong). These situations can be notoriously difficult to diagnose and resolve. The situation is akin to the Schrödinger's cat paradox: the state of our application is changed by our debugging of it -- when we pause our application in a debugger, we interrupt the application in such a way that we do not "see" what is causing the non-deterministic bug.
In this installment of What's Wrong With This Code, we present a simple, common problem that multi-threaded applications face: race conditions.
As a warm-up, please examine the following code, which is adapted from an example in the article "'.NET Threading Part I" by Randy Charles Morin found at http://www.developerfusion.co.uk/show/4134/1/ :
Imports System.Threading
Public Class TaskObject_CreatedByCreateTask_ConsumedByDoTask
Private _id As Integer
Public Sub New(ByVal id As Integer)
_id = id
End Sub
Public ReadOnly Property ID() As Integer
Get
Return (_id)
End Get
End Property
End Class
Public Class Queue
Public QueueLength As Integer
Public IsWaitingForCompletion As Boolean
Public AllThreadsAreDoneEvent As ManualResetEvent
Public Sub New()
QueueLength = 0
End Sub
Public Sub CreateTask(ByVal Obj As TaskObject_CreatedByCreateTask_ConsumedByDoTask)
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoTask), Obj)
QueueLength += 1
End Sub
Public Sub DoTask(ByVal Obj As Object)
Console.WriteLine("Thread {0} uses {1}", _
Thread.CurrentThread.GetHashCode(), _
CType(Obj, TaskObject_CreatedByCreateTask_ConsumedByDoTask).ID)
QueueLength -= 1
If (IsWaitingForCompletion) Then
If (QueueLength = 0) Then AllThreadsAreDoneEvent.Set()
End If
End Sub
Public Sub Wait()
If (QueueLength = 0) Then Exit Sub
AllThreadsAreDoneEvent = New ManualResetEvent(False)
IsWaitingForCompletion = True
AllThreadsAreDoneEvent.WaitOne()
End Sub
End Class
Module SimpleRaceContitionSample
Sub Main()
Dim que As New Queue
For nloop As Integer = 0 To 1000
que.CreateTask(New TaskObject_CreatedByCreateTask_ConsumedByDoTask(nloop))
Next
Console.WriteLine("Main Thread {0}", Thread.CurrentThread.GetHashCode())
que.Wait()
End Sub
End Module

The code listing contains a race condition. If the system switches threads in the
Wait
function sometime after the
If QueueLength = 0
conditional executes and before the
IsWaitingForCompletion = True
assignment, it is possible for the last
DoTask
thread to decrement the
QueueLength
value and exit. Since the final thread exits before the flag is set, the event never fires! Consequently, the
Wait
function never returns and the
Main
routine never exits.
Our design scenario is as follows:
Your company Robert M. Blind has decided to develop a Slot Machine game for sale to retail customers. Another developer at Rob M. Blind, Patrick O'Ferniter, has written code to "spin" the wheels in your virtual slot machine. Your task is to write the UI code to "play" the game. As a first step, you write a simple console application to test interaction with the spinning-wheels code that Paddy O'Ferniter has written (the function called "main" in the VB, C#, and Java code below). However, when you run this code, you receive results similar to the following:
4 5 6
Sum: 15
7 6 7
Sum: 20
1 5 8
Sum: 14
4 7 4
Sum: 15
8 4 3
Sum: 15
6 1 3
Sum: 10
1 5 6
Sum: 12
7 5 8
Sum: 15
This last case is clearly wrong.
What You Know
The class exposes two methods that you can use in your code:
StartSpinning
and
StopSpinning
. You use the former method to start the thread on which the wheel "spins" and the latter to destroy said thread. The
Spin
method is the actual thread method, but you are told that this method will probably be re-scoped as a "private" function (i.e. you should not use it directly in your code).
The design requirements specify that each wheel should always be "spinning" while the game is being played and the read-only property
IsSpinning
exists to allow you to ascertain this in your code. The method also exists to allow you to handle cases where the thread has failed to start. You are also told that each "wheel" will expose an integer-typed read-only property to indicate the "value" at a given time; this method is aptly named
WheelValue
. The following class diagram is given to you as an aid (please ignore the private-scoped members for Parts 1 & 2).
Note: Our example here is a bit contrived -- it has been designed as a teaching case. Also, our wheels don't "spin" in the traditional sense; the values come from a psuedo-random-number generator; there is actually a design reason (of sorts) for this: it is logically conceivable that if all wheel threads received equal attention from the CPU then there would be exactly 9 states that would be displayed in the output -- although this probably won't ever happen on a real single-CPU box, the odds of it happening increase when the code is run on a multi-processor machine operating under minimal load. The thread-scheduling algorithm used by the OS is not within our control; accordingly, we should design our applications in ways that allow for as many scenarios as possible.

Challenge: Part 1
Your company is highly structured in its segregation of programming tasks and your manager assures you that the problem must be in your code. Alter the "main" routine in such a way that it produces reliable results.
Challenge: Part 2
Some users report that the application starts and immediately stops or just seems to hang. Correct the "main" routine to deal with this case.
Challenge: Part 3
"It sounds a dreadful prospect," he commented. "Completely insane, as you say, though it may be the only way out."
-- J. G. Ballard
Paddy has been caught stealing canned air from the company supply room (he apparently has been huffing it while hiding beneath the receptionist's desk). You are given the source code for his SlotMachineWheel class and told to fix it. Assuming that many parts of the application will read the wheel values and only one will change them, eliminate the race condition in the SlotMachineWheel class in a manner that has as minimal an impact upon performance as possible.
Challenge: Part 4
Upon testing, it is discovered that the distribution of wins and losses is not uniformly random. A clever user may notice this pattern and take adavntage of the defect. Fix this.
Challenge: Part 5
Fix any other issues that may exist in the code. Please keep in mind that the class library in which the "wheel" logic resides will be potentially used by many other parts of the application or by other developpers in other projects.
Challenge: Part 6
Since games of chance are regulated by governments and since players who "lose their shirts" tend to be bitter, it is important that this app be "fair" and that it produce results that on average conform to a real (and fair) slot machine.
Calculate the theoretical odds of obtaining "three in a row" (e.g. 1-1-1, 2-2-2, etc.) and of obtaining a "21" (3+9+9, 4+8+9, etc.). Measure the real odds of obtaining these values (i.e. run your code a bunch of times and record the results). Compare the theoretical and the observed distributions. Please include your test stubs in your solution posting.
Code Listings
Visual Basic .NET
Module Module1
Sub Main()
Dim wheels(2) As SlotMachineWheel
wheels(0) = New SlotMachineWheel()
wheels(1) = New SlotMachineWheel()
wheels(2) = New SlotMachineWheel()
wheels(0).StartSpinning()
wheels(1).StartSpinning()
wheels(2).StartSpinning()
Console.WriteLine("Press <space> to get values. Press <Esc> to stop.")
Dim cki As ConsoleKeyInfo
If (wheels(0).IsSpinning And wheels(1).IsSpinning And wheels(2).IsSpinning) Then
Do
cki = Console.ReadKey()
If (cki.Key = ConsoleKey.SpaceBar) Then
For nloop As Integer = 0 To 2
Console.Write(wheels(nloop).WheelValue.ToString() & " ")
Next
Console.WriteLine(ControlChars.CrLf)
Console.WriteLine("Sum: " & _
(wheels(0).WheelValue + _
wheels(1).WheelValue() + _
wheels(2).WheelValue).ToString())
Console.WriteLine(ControlChars.CrLf)
End If
Loop While (cki.Key <> ConsoleKey.Escape)
For nloop As Integer = 0 To 2
wheels(nloop).StopSpinning()
Next
End If
End Sub
End Module
Imports System.Threading
Public Class SlotMachineWheel
Private _value As Integer
Private _thread As New Thread(AddressOf Spin)
Private _bcontinuespinning As Boolean = False
Private _isspinning As Boolean = False
Public ReadOnly Property WheelValue() As Integer
Get
Return (_value)
End Get
End Property
Public ReadOnly Property IsSpinning() As Boolean
Get
Return (_isspinning)
End Get
End Property
Public Sub New()
_value = 0
End Sub
Public Sub StartSpinning()
_bcontinuespinning = True
If (_thread.ThreadState = ThreadState.Unstarted) Then
Try
_thread.Start()
Catch secex As Security.SecurityException
Console.WriteLine(secex.Message)
Exit Sub
Catch memex As OutOfMemoryException
Console.WriteLine(memex.Message)
Exit Sub
End Try
End If
End Sub
Public Sub Spin()
Dim __randomnumber As New Random(Now.Millisecond)
_isspinning = True
Do While _bcontinuespinning
_value = __randomnumber.Next(1, 9)
Loop
_isspinning = False
End Sub
Public Sub StopSpinning()
If (_thread.ThreadState = ThreadState.Running) Then
_bcontinuespinning = False
End If
End Sub
End Class
C# Source
using System;
using System.Collections.Generic;
using System.Text;
namespace WWWTCSlotMachineCS
{
class Program
{
static void Main ( string[] args )
{
SlotMachineWheel[] wheels = new SlotMachineWheel[3];
wheels[0] = new SlotMachineWheel ( );
wheels[1] = new SlotMachineWheel ( );
wheels[2] = new SlotMachineWheel ( );
wheels[0].StartSpinning();
wheels[1].StartSpinning();
wheels[2].StartSpinning();
Console.WriteLine("Press <space> to get values. Press <Esc> to stop.");
ConsoleKeyInfo cki;
if ((wheels[0].IsSpinning & wheels[1].IsSpinning & wheels[2].IsSpinning)) {
do {
cki = Console.ReadKey();
if ((cki.Key == ConsoleKey.SpaceBar)) {
for (int nloop = 0; nloop <= 2; nloop++) {
Console.Write(wheels[nloop].WheelValue.ToString() + " ");
}
Console.WriteLine("\n");
Console.WriteLine("Sum: " +
(wheels[0].WheelValue +
wheels[1].WheelValue +
wheels[2].WheelValue).ToString());
Console.WriteLine("\n");
}
} while ((cki.Key != ConsoleKey.Escape));
for (int nloop = 0; nloop <= 2; nloop++) {
wheels[nloop].StopSpinning();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace WWWTCSlotMachineCS
{
public class SlotMachineWheel
{
private int _value;
private bool _bcontinuespinning = false;
private bool _isspinning = false;
private Thread _thread;
public int WheelValue
{
get
{
return ( _value );
}
}
public bool IsSpinning
{
get
{
return ( _isspinning );
}
}
public SlotMachineWheel ( )
{
_value = 0;
_thread = new Thread(new ThreadStart(Spin));
}
public void StartSpinning ( )
{
_bcontinuespinning = true;
if ( ( _thread.ThreadState == ThreadState.Unstarted ) )
{
try
{
_thread.Start ( );
}
catch ( System.Security.SecurityException secex )
{
Console.WriteLine ( secex.Message );
return;
}
catch ( OutOfMemoryException memex )
{
Console.WriteLine ( memex.Message );
return;
}
}
}
public void Spin ( )
{
Random __randomnumber = new Random ( DateTime.Now.Millisecond );
_isspinning = true;
while ( _bcontinuespinning )
{
_value = __randomnumber.Next ( 1, 9 );
}
_isspinning = false;
}
public void StopSpinning ( )
{
if ( ( _thread.ThreadState == ThreadState.Running ) )
{
_bcontinuespinning = false;
}
}
}
}
Java 1.5 Source
import java.util.Random;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
public class SlotMachineWheel implements Runnable
{
private int value_ = 0;
private boolean isSpinning_ = false;
private boolean continueSpinning_ = false;
public static void main(String[] args)
{ SlotMachineWheel[] wheels = new SlotMachineWheel[3];
for (int i = 0; i < wheels.length; i++)
wheels[i] = new SlotMachineWheel();
for (SlotMachineWheel w : wheels)
new Thread(w).start();
for (SlotMachineWheel w : wheels)
if (!w.isSpinning())
{ for (SlotMachineWheel w2 : wheels)
w2.stopSpinning();
return;
}
System.out.println("Press 0 to get values. Press 1 to stop.");
Reader in = new InputStreamReader(System.in);
char input;
do
{ try
{ input = (char)in.read();
}
catch (IOException exc)
{ System.out.println(exc);
input = '1';
}
if (input == '0')
{ System.out.println(Arrays.asList(wheels));
int sum = 0;
for (SlotMachineWheel w : wheels)
sum += w.getWheelValue();
System.out.println("Sum: " + sum);
System.out.println();
}
} while (input != '1');
for (SlotMachineWheel w : wheels)
w.stopSpinning();
}
public int getWheelValue()
{ return value_;
}
public boolean isSpinning()
{ return isSpinning_;
}
public void reset()
{ value_ = 0;
}
public void run()
{ continueSpinning_ = true;
isSpinning_ = true;
Random r = new Random();
while (continueSpinning_)
value_ = 1 + r.nextInt(9);
isSpinning_ = false;
}
public void stopSpinning()
{ continueSpinning_ = false;
}
public String toString()
{ return getWheelValue() + "";
}
}
C Source by bulibuta
NOTE: this is posix enviorment dependent, pthread libs are needed and has been tested with gcc 3.3.6 under linux kernel 2.4.31 and 2.6.11
slot_machine_wheel.h
#include <pthread.h>
typedef struct slot_machine_wheel
{
int _value;
int _continue_spinning;
int _is_spinning;
pthread_t _thread;
} slot_machine_wheel_t;
slot_machine_wheel_lib.c
#include <stdio.h>
#include <time.h>
#include "slot_machine_wheel.h"
void* Spin( void* );
int WheelValue( slot_machine_wheel_t* this )
{
return ( this->_value );
}
int IsSpinning( slot_machine_wheel_t* this )
{
printf("%d\n",this->_is_spinning); //LINE #01
return ( this->_is_spinning );
}
void SlotMachineWheel( slot_machine_wheel_t* this )
{
this->_value = 0;
this->_is_spinning = 0;
this->_continue_spinning = 0;
}
void StartSpinning( slot_machine_wheel_t* this )
{
this->_continue_spinning = 1;
if( pthread_create( &this->_thread, NULL, Spin, (void*)this ) )
{
printf( "pthread creation failed!\n" );
exit( 1 );
}
}
void* Spin( void* ptr )
{
slot_machine_wheel_t* this = ( slot_machine_wheel_t* )ptr;
srandom( time( NULL ) );
this->_is_spinning = 1;
while( this->_continue_spinning )
this->_value = random()%10;
this->_is_spinning = 0;
}
void StopSpinning( slot_machine_wheel_t* this )
{
this->_continue_spinning = 0;
pthread_join( this->_thread, NULL );
}
slot_machine_wheel_main.c
#include <stdio.h>
#include "slot_machine_wheel.h"
int main( int argc, char* argv[] )
{
int nloop;
char cki;
slot_machine_wheel_t wheels[3];
SlotMachineWheel( &wheels[0] );
SlotMachineWheel( &wheels[1] );
SlotMachineWheel( &wheels[2] );
StartSpinning( &wheels[0] );
StartSpinning( &wheels[1] );
StartSpinning( &wheels[2] );
printf( "Press <space> to get values. Press 'q' to stop.\n" );
if( IsSpinning( &wheels[0] ) & IsSpinning( &wheels[1] ) & IsSpinning( &wheels[2] ))
{
do
{
cki = getc( stdin );
if( cki == ' ' )
{
for ( nloop = 0; nloop <= 2; nloop++ )
{
printf( "%d ", WheelValue( &wheels[ nloop ] ) );
}
printf("\n");
printf( "Sum: %d\n",WheelValue( &wheels[0] ) + WheelValue( &wheels[1] ) + WheelValue( &wheels[2] ));
}
}while( cki != 'q' );
for( nloop = 0; nloop <= 2; nloop++ )
StopSpinning( &wheels[ nloop ] );
}
}
C++ ZThreads
#include <iostream>
#include <numeric>
#include <functional>
#include <ctime>
#include <cstdlib>
#include "zthread/Thread.h"
class slot_machine_wheel : public ZThread::Runnable
{
public:
void run()
{ is_spinning_ = continue_spinning_ = true;
srand(time(NULL));
while (continue_spinning_)
value_ = 1 + rand() % 9;
is_spinning_ = false;
}
int get_wheel_value() const
{ return value_;
}
bool is_spinning() const
{ return is_spinning_;
}
void reset()
{ value_ = 0;
}
void stop_spinning()
{ continue_spinning_ = false;
}
private:
int value_;
bool is_spinning_;
bool continue_spinning_;
};
std::ostream& operator<<(std::ostream& out, const slot_machine_wheel& w)
{ return out << w.get_wheel_value();
}
int main()
{ using namespace std;
using namespace ZThread;
const size_t WHEELS_SIZE = 3;
slot_machine_wheel* wheels[WHEELS_SIZE];
try
{ for (int i = 0; i < WHEELS_SIZE; i++)
{ wheels[i] = new slot_machine_wheel;
Thread t(wheels[i]);
}
}
catch (const Synchronization_Exception& exc)
{ cerr << exc.what();
return (-1);
}
for (int i = 0; i < WHEELS_SIZE; i++)
if (not wheels[i]->is_spinning())
{ for_each(wheels, wheels + WHEELS_SIZE, mem_fun(&slot_machine_wheel::stop_spinning));
return (-1);
}
cout << "Enter 0 to get values. Enter 1 to quit" << endl;
char cki;
do
{ cin.get(cki);
if (cki == '0')
{ for (int i = 0; i < WHEELS_SIZE; i++)
cout << *wheels[i] << " ";
cout << "\n";
cout << "Sum: ";
unsigned int sum = 0;
for (int i = 0; i < WHEELS_SIZE; i++)
sum += wheels[i]->get_wheel_value();
cout << sum;
}
cout << endl;
} while (cki != '1');
for_each(wheels, wheels + WHEELS_SIZE,
mem_fun(&slot_machine_wheel::stop_spinning));
}
C++/Win32 Source by rattle
wheel.hpp
#pragma once
#include <windows.h>
class CSpinningWheel {
public:
CSpinningWheel()
: m_dwWheel(0),
m_dwModule(0),
m_bSpinning(FALSE),
m_dwThreadID(0),
m_hThread(INVALID_HANDLE_VALUE)
{
SeedInit();
};
public:
virtual VOID StopSpinning();
virtual BOOL StartSpinning(DWORD dwWheel=10);
virtual BOOL DoOneSpin();
public:
inline BOOL IsSpinning() { return m_bSpinning; }
inline DWORD GetWheelValue() { return m_dwWheel; }
private:
DWORD m_dwWheel;
DWORD m_dwModule;
DWORD m_dwThreadID;
DWORD m_dwSeed;
BOOL m_bSpinning;
HANDLE m_hThread;
protected:
virtual DWORD GetRandom(DWORD dwMax);
virtual VOID SeedInit();
static DWORD __stdcall _Thread(PVOID pSpin);
};
wheel.cpp
#include "wheel.hpp"
#define LUBE 0
VOID CSpinningWheel::StopSpinning()
{
if (m_hThread == INVALID_HANDLE_VALUE)
return;
m_bSpinning = FALSE;
if (WaitForSingleObject(m_hThread, 100))
TerminateThread(m_hThread,0);
CloseHandle(m_hThread);
m_hThread = INVALID_HANDLE_VALUE;
}
BOOL CSpinningWheel::StartSpinning(DWORD dwWheel) {
if (IsSpinning())
return (m_bSpinning=FALSE);
m_dwModule = dwWheel;
m_hThread = CreateThread(
NULL, 0, &CSpinningWheel::_Thread,
(PVOID) this, 0, &m_dwThreadID);
if (m_hThread != INVALID_HANDLE_VALUE)
m_bSpinning= TRUE;
return m_bSpinning;
}
VOID CSpinningWheel::SeedInit() {
m_dwSeed = GetTickCount();
if (!(m_dwSeed%3)) m_dwSeed = ~m_dwSeed;
if (!(m_dwSeed%2)) m_dwSeed = (m_dwSeed&0xFFFF)+(m_dwSeed<<16);
Sleep(5);
}
BOOL CSpinningWheel::DoOneSpin()
{
if (!m_bSpinning) {
return FALSE;
} else {
m_dwWheel = GetRandom(m_dwModule-1)+1;
return TRUE;
} }
DWORD CSpinningWheel::GetRandom(DWORD dwMax) {
m_dwSeed=0x343FD*m_dwSeed+0x269EC3;
return ((m_dwSeed>>16)&0x7fff) % dwMax;
}
DWORD __stdcall CSpinningWheel::_Thread(PVOID pSpin) {
while (((CSpinningWheel*) pSpin)->DoOneSpin()) ;
return 0;
}
spin.cpp
#include "wheel.hpp"
#include <stdio.h>
#include <conio.h>
int main(int argc, char** argv) {
signed int i = 0, j, w=0;
CSpinningWheel* wheels;
if (argc>1) w=atoi(argv[1]);
w=w?w:3;
wheels = new CSpinningWheel[w];
for (i=0;i<w;i++)
if (!(wheels[i]=CSpinningWheel()).StartSpinning(10))
return 0xBAD;
printf("\n"
"**************** Spinning WHEELS! ****\n"
"** **\n"
"** press ANYKEY to get a result, **\n"
"** press ESC to exit. ENJOY! **\n"
"** **\n"
"**************************************\n"
"\n Wheels Spinning - ");
while (getch()!=0X1B) {
for (i=0;i<w;printf("%d ",wheels[i++].GetWheelValue())) ; // print values
for (i=0,j=0;i<w;j+=wheels[i++].GetWheelValue()) ; // sum up
printf("- that's %d!\n Wheels Spinning - ", j);
}
printf ("bye bye!\n\n");
for (i=0;i<w;i++)
wheels[i].StopSpinning();
delete[] wheels;
return 0;
}
C++ Boost.Thread
#include <iostream>
#include <algorithm>
#include <iterator>
#include <ctime>
#include <cstdlib>
#include "boost/thread.hpp"
#include "boost/ref.hpp"
#include "boost/mem_fn.hpp"
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
class slot_machine_wheel
{
public:
void operator()()
{ is_spinning_ = continue_spinning_ = true;
srand(time(NULL));
while (continue_spinning_)
value_ = 1 + rand() % 9;
is_spinning_ = false;
}
int get_wheel_value() const
{ return value_;
}
bool is_spinning() const
{ return is_spinning_;
}
void reset()
{ value_ = 0;
}
void stop_spinning()
{ continue_spinning_ = false;
}
private:
int value_;
bool is_spinning_;
bool continue_spinning_;
};
std::ostream& operator<<(std::ostream& out, const slot_machine_wheel& w)
{ return out << w.get_wheel_value();
}
int main()
{ using namespace std;
using namespace boost;
using namespace boost::lambda;
const size_t WHEELS_SIZE = 3;
slot_machine_wheel wheels[WHEELS_SIZE];
for (int i = 0; i < WHEELS_SIZE; i++)
thread t(ref(wheels[i]));
for (int i = 0; i < WHEELS_SIZE; i++)
if (not wheels[i].is_spinning())
{ for_each(wheels, wheels + WHEELS_SIZE,
mem_fn(&slot_machine_wheel::stop_spinning));
return (-1);
}
cout << "Enter 0 to get values. Enter 1 to quit" << endl;
char cki;
do
{ cin.get(cki);
if (cki == '0')
{ copy(&wheels[0], &wheels[WHEELS_SIZE],
ostream_iterator<slot_machine_wheel>(cout, " "));
cout << "\n";
cout << "Sum: ";
unsigned int sum = 0;
for_each(&wheels[0], &wheels[WHEELS_SIZE],
sum += bind(&slot_machine_wheel::get_wheel_value, _1));
cout << sum;
}
cout << endl;
} while (cki != '1');
for_each(wheels, wheels + WHEELS_SIZE,
mem_fn(&slot_machine_wheel::stop_spinning));
}
Answers
A candle loses nothing by lighting another candle. -- James Keller
Please endeavor to post answers from which others might learn, ask questions about what others have attempted, etc. Thanks!
Try It Yourself
A man who follows someone else: not only does he not find anything, he is not even looking. -- Seneca
Even if someone else has solved this challenge, try it for yourself. Experience is the best teacher -- there are several correct answers for this exercise and the requirement of Part 3 is not as easy as it may appear at first glance.
FYI: The .NET examples can be compiled with the free compilers that come with the .NET SDK; the Java sample can likewise be compiled using any Java 1.5 compatible compiler. The C++ version will require either the ZThreads library or Boost and a relatively recent standards compliant compiler (such as MS VC++ .NET 7.1 and/or GCC 3+). (Please post a comment if you need help compiling the sample code).
Some References
Secure Programming for Linux and Unix HOWTO Avoid Race Conditions
IBM Secure programmer: Prevent race conditions
Thread-Specific Storage
An Object Behavioral Pattern for Efficiently Accessing per-Thread State (pdf)
Developer Fusion: .NET Threading Part I - Introduction (.NET)
If you find any other good references, please share them in the comments. Thanks.
Our Thanks
Please join us in thanking the staff here at code.box.sk for their help in preparing this article. Their feedback has been invaluable to us.