#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char work_area[WORK_SIZE];
void *thread_start(void *);
int main() {
pthread_t a_thread;
pthread_mutex_init(&work_mutex,NULL);
pthread_create(&a_thread,NULL,thread_start,NULL);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("Enter some text\n");
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
sleep(1);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("You enetered %d char",strlen(work_area));
pthread_mutex_unlock(&work_mutex);
}
}
When I execute the program, after releasing of the mutex lock in main thread, it again aquires the lock, everytime, before the second thread could aquire the lock. I was expecting that once the main thread would release the lock, the second thread which is already blocked would aquire th开发者_如何转开发e lock and start execution before the main.
To be more clear, I am getting such type of output :-
Enter some text
qwerty
Enter some text
asdaf
Enter some text
jkdf
Enter some text
It just seems that way to you. You lock before doing data entry in main which is going to be orders of magnitude greater than what it take to output the line. In all that time the other thread will do nothing but block. Your main is going to release the lock and microseconds later acquire it again.
If you do this long enough - maybe thousands of times - you will see it work. But it would be better to just copy the input line in main to a queue or some other piece of memory protect by the lock. Then the other thread would have a chance to get at it.
EDIT:
The general idea is this. My code additions are terrible but should work well enough for illustration.
int main()
{
pthread_t a_thread;
pthread_mutex_init(&work_mutex, NULL);
pthread_create(&a_thread, NULL, thread_start, NULL);
memset(work_area, '\0', sizeof(work_area));
char input[WORK_SIZE - 1];
while (1)
{
printf("Enter some text\n");
fgets(input, WORK_SIZE, stdin);
pthread_mutex_lock(&work_mutex);
strcpy(work_area, input);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
sleep(1);
while (1)
{
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '\0')
{
printf("You enetered %d char\n", strlen(work_area));
work_area[0] = '\0';
}
pthread_mutex_unlock(&work_mutex);
}
}
Putting asside the suggestion to use a semaphore, I think that the reason for the behaviour that you are observing is as follows.
- Until the
pthread_mutex_lock
in thread_start is called, the loop in main won't be blocked. - Therefore, the only time that thread_start's loop will get a chance to call
pthread_mutex_lock
is when the time slice of the thread executing main expires - The chances of that time slice expiry occuring while the lock is released is miniscule. This is because the main thread will probably have a fresh time slice when it wakes from the blocked state while it waited for the ENTER key.
Note, this explanation assumes a single core. But even on a multicore system the thread_start thread is only going to be scheduled when another thread's time slice runs out. The chances of that happening while main's thread isn't holding the lock is small.
One possible test for my above hypothesis would be to call pthread_yield after releasing the lock. You'll probably want to do that in both threads. Even then I don't think it will GUARANTEE a thread switch every time.
You may want to create and initialise a semaphore and then wait in the 2nd thread for the main function to signal a event when a input is fed to it.
Check conditional wait and semaphores.
The second thread doesnt know what event is generated in the main thread. For your reference.
After reading the comments given by torak, I changed the code. Now it is working fine as expected.
[root@localhost threads]# diff -Nurp mutex.c mutex_modified.c
--- mutex.c 2010-07-09 19:50:51.000000000 +0530
+++ mutex_modified.c 2010-07-09 19:50:35.000000000 +0530
@@ -18,6 +18,7 @@ pthread_mutex_lock(&work_mutex);
printf("Enter some text\n");
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
+sleep(1);
}
return 0;
}
@@ -30,5 +31,6 @@ while(1)
pthread_mutex_lock(&work_mutex);
printf("You enetered %d char",strlen(work_area));
pthread_mutex_unlock(&work_mutex);
+sleep(1);
}
}
It is still very confusing though. Although it was a test program, what should one do to avoid such kind of situation while coding a real application?
Here's a slightly more awkward solution that's guaranteed to work: #include
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char input_area[WORK_SIZE];
char work_area[WORK_SIZE];
void *thread_start(void *);
int main()
{
pthread_t a_thread;
pthread_mutex_init(&work_mutex,NULL);
work_area[0] = 0;
pthread_create(&a_thread,NULL,thread_start,NULL);
while(1) {
printf("Enter some text\n");
fgets(input_area, WORK_SIZE, stdin);
pthread_mutex_lock(&work_mutex);
while (work_area[0] != 0) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
memcpy(work_area, input_area, WORK_SIZE);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
while(1)
{
pthread_mutex_lock(&work_mutex);
if (work_area[0] > 0) {
printf("you enetered %d char\n",strlen(work_area));
work_area[0] = 0;
pthread_mutex_unlock(&work_mutex);
} else {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
}
}
Note that because POSIX mutex locks are thread-specific, the consumer thread can't wait on a signal from the producer thread, so here it just wakes up every second to check for new data. Similarly, if the producer needs to put something in queue and the work area is full, it waits by seconds until the consumer gets around to emptying the buffer. You could get rid of some delays by using pthread_yield
instead of sleep
, but then your threads will 'busy-wait', consuming lots of CPU to check again and again for their condition to be met
Note that if you want it to print a line for 0-character entries you could add a separate bool indicating whether there's new data in the queue.
@torax *"Note, this explanation assumes a single core. But even on a multicore system the thread_start thread is only going to be scheduled when another thread's time slice runs out"*
I think this is not true, the main thread and the thread_start can be scheduled parallely on both cores
精彩评论