I'm new in c and trying to get understand a piece of code now. It is about strict alternation for Pthreads.
line 1: #include <iostream>
line 2: #include <pthread.h>
line 3: #include <stdlib.h>
line 4:
line 5: int count;
line 6: int turn = 0;
line 7:
line 8: void* function(void* arg){
line 9: int actual_arg = *((int*) arg);
line 10: for(unsigned int i = 0; i < 10; i) {
line 11: while(turn != actual_arg);
line 12: count
line 13: std::cout << "Thread #" << actual_arg << " count = " << count << std::endl;
line 14: if(actual_arg==0){
line 15: turn =1;
line 16: }else {
line 17: turn =0;
line 18: }
line 19: int max = rand() % 100000;
line 20: for(int x = 0; x < max; x );
line 21: }
line 22: pthread_exit(NULL);
line 23: }
My question is: Why the first for loop use unsigned int(line 10), is that because of the declaration of the pointer value arg in line 9? What is line 11? I kind of know it is about checking thread in actual_arg, however, I am still confused for that line. For lines 14 to 18, since the turn value will only be 1 or 0 so we use an if statement to check the value here? The last one is for lines 19 and 20, are we really need those codes? what is that for then?
Thank you for answering these questions, and very grateful if you can help me interpret this code in detail.
CodePudding user response:
I don't know if this is your code or someone else's, but it has issues.
line 5: int count;
line 6: int turn = 0;
line 7:
line 8: void* function(void* arg){
line 9: int actual_arg = *((int*) arg);
line 10: for(unsigned int i = 0; i < 10; i) {
line 11: while(turn != actual_arg);
line 12: count
line 13: std::cout << "Thread #" << actual_arg << " count = " << count << std::endl;
line 14: if(actual_arg==0){
line 15: turn =1;
line 16: }else {
line 17: turn =0;
line 18: }
line 19: int max = rand() % 100000;
line 20: for(int x = 0; x < max; x );
line 21: }
line 22: pthread_exit(NULL);
line 23: }
You asked about this:
line 10: for(unsigned int i = 0; i < 10; i) {
This could have been an int
or unsigned int
because it's never going to be negative. It has absolutely nothing to do with anything else. The variable itself is just there to ensure you loop 10 times.
What does the rest do?
We'll let's say we're starting two threads, 0 and 1.
line 11: while(turn != actual_arg);
line 12: count ;
(You're missing the ;
on that, BTW).
This is called a busy-sleep. It's BAD. It chews massive amounts of CPU while waiting for your turn. There are better ways. However, thread 0 will just step past this. Thread 1 will wait.
line 13: std::cout << "Thread #" << actual_arg << " count = " << count << std::endl;
Thread 1 is still waiting, but thread 0 will print out an unknown count -- because thread 1 is spinning like crazy, and this is the count of the amount of spinning that happens. count is going to get big REALLY FAST and probably roll over before your 10 loops are done.
line 14: if(actual_arg==0){
line 15: turn =1;
line 16: }else {
line 17: turn =0;
line 18: }
This just flops turn back and forth between 0 and 1. Thread 0 will set the turn to 1 so that thread 1 can run.
line 19: int max = rand() % 100000;
line 20: for(int x = 0; x < max; x );
This is another horrible busy-sleep. It's an attempt to chew time but it also chews an enormous amount of CPU. Again. DON'T DO THIS. Use a sleep method.
I don't know where you got this code, but this is a horrible example of how to do multithreading. Do NOT use this as your learning example.
CodePudding user response:
Why the first for loop use unsigned int(line 10), is that because of the declaration of the pointer value arg in line 9?
No particular reason. int
or short
or unsigned long long
or any other integer type would have worked fine for the purpose. I can't speak to the decision process of the author of that code as far as choosing unsigned int
in particular. I would have used int
, myself, but there's nothing particularly wrong with using unsigned int
.
What is line 11? I kind of know it is about checking thread in actual_arg, however, I am still confused for that line.
It loops until the condition turn != actual_arg
evaluates to 0 (false). That appears to be intended to make the thread busy-wait until its turn comes, but
- That's hideous, and
- It contains a data race, and therefore might very well not work at all.
A more appropriate thing to do here would be to use a condition variable to help the thread suspend operation until its turn arrives. That would also involve a mutex, which would, among other things, serve to protect the shared data and therefore avoid the data race.
For lines 14 to 18, since the turn value will only be 1 or 0 so we use an if statement to check the value here?
Basically, yes. That code appears intended to set turn
to indicate that it is now the other thread's turn, but this involves another data race. The turn
variable must be protected from concurrent access by multiple threads via a mutex or a similar synchronization object.
The last one is for lines 19 and 20, are we really need those codes? what is that for then?
The intention appears to be make the thread spend a random amount of time before looping back around to try to take another turn. Perhaps this is intended to simulate a bona fide computational workload. However, this is not certain to be effective, as a compiler could easily optimize out the whole for
loop. In the event that it is effective, it is extremely wasteful.
Overall, I concur with your other answer: the code presented is awful. Throw it away. Do not take it as a good example of anything.