In: Computer Science
Explain the multithreading issue in the following c++ code and fix it. (consider options such as mutex lock). #include <iostream> #include <thread> #include <string> #include <unistd.h> using namespace std; static int tickets = 32; static int sale = 0; class Seller { public: void Main() { for (;;) { if(tickets <= 0){ cout << "In Seller[" << ID <<"] sold out"<< endl; break; }else{ tickets--; sale++; usleep(1); cout << "In Seller[" << ID <<"]"<<sale<< endl; } } } int ID; }; int main() { Seller st1; st1.ID = 1; Seller st2; st2.ID = 2; Seller st3; st3.ID = 3; Seller st4; st4.ID = 4; Seller st5; st5.ID = 5; thread th1(&Seller::Main, &st1); thread th2(&Seller::Main, &st2); thread th3(&Seller::Main, &st3); thread th4(&Seller::Main, &st4); thread th5(&Seller::Main, &st5); th1.join(); th2.join(); th3.join(); th4.join(); th5.join(); return 0; }
If there is no such locking condition involved in the above program all the threads seller 1 , 2, 3, 4, 5 we will begin parallely executing the main program and two or more seller might be selling same ticket to the customer which causes violation . If run the above program without any mutex lock output would look like
In Seller[4]11
In Seller[3]11
In Seller[2]11
In Seller[5]16
In Seller[4]17
In Seller[3]18
In Seller[2]19
In Seller[1]20
In Seller[5]21
In Seller[4]22
In Seller[3]23
In Seller[2]24
In Seller[1]25
In Seller[5]26
In Seller[4]27
In Seller[3]28
In Seller[2]29
In Seller[1]30
In Seller[5]31
In Seller[4]32
In Seller[4] sold out
In Seller[5]32
In Seller[5] sold out
In Seller[3]32
In Seller[3] sold out
In Seller[2]32
In Seller[2] sold out
In Seller[1]32
In Seller[1] sold out
As you can see in the above output ticket 11 is sold by sellers 4 , 3, 2 which is not aceptable. Now let us try to modify the above program by using mutex lock and unlock. Locking should be done before code where the race condition begins and after the code finishes unlock should be done.
Below is the following code and updated part of code is in bold. Here we try to keep lock variable before booking process i.e., before if statement and unlock after else statement completion. And we are keeping unlock again after for loop because if tickets are sold out loop is breaked and execution goes to end of for loop without passing through else block. By doing so other sellers can also get to know that tickets have been sold out .
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <unistd.h>
using namespace std;
static int tickets = 32;
static int sale = 0;
std::mutex mtx;
class Seller {
public:
void Main()
{
for (;;)
{
mtx.lock();
if(tickets <= 0){
cout << "In Seller[" << ID <<"] sold out"<< endl;
break;
}else{
tickets--;
sale++;
usleep(1);
cout << "In Seller[" << ID <<"]"<<sale<< endl;
}
mtx.unlock();
}
mtx.unlock();
}
int ID;
};
int main()
{
Seller st1;
st1.ID = 1;
Seller st2;
st2.ID = 2;
Seller st3;
st3.ID = 3;
Seller st4;
st4.ID = 4;
Seller st5;
st5.ID = 5;
thread th1(&Seller::Main, &st1);
thread th2(&Seller::Main, &st2);
thread th3(&Seller::Main, &st3);
thread th4(&Seller::Main, &st4);
thread th5(&Seller::Main, &st5);
th1.join();
th2.join();
th3.join();
th4.join();
th5.join();
return 0;
}
Below is screenshot of output of above code for proof of work.