Threads Don't Take Turns Under Linux (Ubuntu Gutsy Gibbon)

I apologize if this gets posted more than once. I found the group on
GMane, tried to post, joined the email list, then tried to post several
times but still have yet to see it show up.
I'm learning C++ and found a tutorial on threads with Boost on Dr.
Dobb's site. I've copied the code from this url:
http://www.ddj.com/cpp/184401518?pgno=5
It's supposed to fill a buffer with data on one thread and remove data
on the other thread. I would expect it to add data and remove at
almost the same time but when I run it, it adds 10 items, then removes
them, then adds ten again, and so on. It's never adding and removing
in an "interleaved" fashion. I've also tried the same with simple
counters where both threads count to ten, but it doesn't seem to make a
difference. It seems no matter how I set it up, one thread either
completes, or runs until it has to stop for a mutex, then the other
thread runs. It seems that there is never a case of both threads
running at the same time. I've never had trouble doing this in Perl
with fork() so I'm not at all sure what could be going wrong.
As I said, it's not just the program below, it seems to be any program
that uses more than one thread.
Am I doing something wrong or is there a setting I need to change?
I'm compiling it with "g++ -lboost_thread mthrd.cpp -o mthrd
&& ./mthrd";
Thanks!
Hal
------
#include

On Mon, Mar 17, 2008 at 09:42:46AM -0400, Hal Vaughan wrote:
Am I doing something wrong or is there a setting I need to change?
That's normal behavior; threads are scheduled by the OS. In your case, the thread fills the buffer faster then its timeslice expires, so you see buffer being filled/emptied in "batches". If you want them to be interleaved, lower the buffer size or try running on an SMP machine.

-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Zeljko Vrba Sent: 17 March 2008 15:04 To: boost-users@lists.boost.org Subject: Re: [Boost-users] Threads Don't Take Turns Under Linux (UbuntuGutsy Gibbon)
On Mon, Mar 17, 2008 at 09:42:46AM -0400, Hal Vaughan wrote:
Am I doing something wrong or is there a setting I need to change?
That's normal behavior; threads are scheduled by the OS. In your case, the thread fills the buffer faster then its timeslice expires, so you see buffer being filled/emptied in "batches". If you want them to be interleaved, lower the buffer size or try running on an SMP machine.
Or put a usleep(0) in the thread to force the timeslice to expire? IIRC. James This message (including any attachments) contains confidential and/or proprietary information intended only for the addressee. Any unauthorized disclosure, copying, distribution or reliance on the contents of this information is strictly prohibited and may constitute a violation of law. If you are not the intended recipient, please notify the sender immediately by responding to this e-mail, and delete the message from your system. If you have any questions about this e-mail please notify the sender immediately.

On Mon, Mar 17, 2008 at 04:09:53PM +0100, Hughes, James wrote:
Or put a usleep(0) in the thread to force the timeslice to expire? IIRC.
Calling sched_yield() would also probably help, but that's a non-solution to the OP's non-problem. To the OP: if a particular order of execution is desired, it has to be explicitly coded with various synchronization primitives.

Zeljko Vrba wrote:
On Mon, Mar 17, 2008 at 04:09:53PM +0100, Hughes, James wrote:
Or put a usleep(0) in the thread to force the timeslice to expire? IIRC.
Calling sched_yield() would also probably help, but that's a non-solution to the OP's non-problem. To the OP: if a particular order of execution is desired, it has to be explicitly coded with various synchronization primitives.
It's not about order of execution. I explained it in another post, but it didn't come through right away due to moderation. (Are all posts directly to the news group moderated? Do I have to post via email for more timely posting?) My concern, when I saw one thread execute completely then another is that it seems one thread hogs the resources, even if it's not doing heavy work. Since my main task, in the actual program I'm writing, is to monitor something and report all output on one thread and to have another to allow commands to go to the device, I want to be sure that the monitoring thread won't be blocked while data it needs is coming in, but I don't want that thread to block the other one while it (the monitoring thread) is waiting for incoming data. Hal

On Mon, Mar 17, 2008 at 9:50 AM, Hal Vaughan
...I want to be sure that the monitoring thread won't be blocked while data it needs is coming in, but I don't want that thread to block the other one while it (the monitoring thread) is waiting for incoming data.
If you're doing synchronous reads and there is no data, then the reading thread will block (e.g. take no cpu cycles) until there is something to read. Jon

Zeljko Vrba wrote:
On Mon, Mar 17, 2008 at 09:42:46AM -0400, Hal Vaughan wrote:
Am I doing something wrong or is there a setting I need to change?
That's normal behavior; threads are scheduled by the OS. In your case, the thread fills the buffer faster then its timeslice expires, so you see buffer being filled/emptied in "batches". If you want them to be interleaved, lower the buffer size or try running on an SMP machine.
So when I did just a loop in a routine that printed numbers 1-10 (or, when I experimented, 1-1000), and thread 1 always finished completely before thread 2 could start, is that the same? Is there any way I can get the two threads to run concurrently? (I know true multi-tasking on a single CPU is just slicing up time, but by concurrently, I mean sharing resources instead of one taking up everything.) This is just a test program, but my big concern is that the program I'm writing does something like this. I'm controlling an HD radio through the serial port (or a usb port with a usb->RS232 adaptor). I know once I mention ports, I'm getting into OS specific stuff, but even beyond that issue, I'm trying to understand what I need to do and what's going on. When I'm done, I want to have one thread that listens to the port and uses all the incoming data to check things like the current station, track title, artist name, and so on. To read the port this way, I'm using an ifstream object to open the port as a file, which seems to work well because it's buffered. Then, in another thread, I write the commands to the port as a "regular" file. This all goes in a library where the listening thread can be started and stopped. I don't want to miss incoming data on the ifstream object, but it seems if I let that thread run on it's own, the other thread to pass on commands won't get CPU time to run and any program using this lib will be locked up as long as I'm listening to the port. I know in Perl I can fork and have one thread continue to do it's own thing without it seeming to effect the other. I also know Perl is a much higher level language, but I was hoping there might be a way, in C++, to spin off this listener thread, let it keep listening and processing the data, and have it stay in the background so other threads can continue. If I can't do that, then I have to write this part of the program as some kind of daemon so it gets it's own process id and stays separate from the main program. So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP? (This is going to be for carputers, so it won't be in an SMP environment.) Thanks for any help on this, links for where to look for more info, and so on! Hal

On Mon, Mar 17, 2008 at 11:17:59AM -0400, Hal Vaughan wrote:
thread 2 could start, is that the same? Is there any way I can get the two threads to run concurrently? (I know true multi-tasking on a single CPU is
Yes, you need to properly synchronize between them. Thread A has to explicitly signal thread B to start running (while A puts itself to sleep). If you don't use such signalling, the OS has all right to assume that you don't care about the scheduling order.
just slicing up time, but by concurrently, I mean sharing resources instead of one taking up everything.)
"Sharing resources" is very vaguely defined. If thread A runs for 30 seconds, and then B for 30 seconds without interruption, they will still have received their fair share of resources if the total run time of the program is, say, 1 hour.
This is just a test program, but my big concern is that the program I'm writing does something like this. I'm controlling an HD radio through the serial port (or a usb port with a usb->RS232 adaptor). I know once I mention ports, I'm getting into OS specific stuff, but even beyond that issue, I'm trying to understand what I need to do and what's going on.
title, artist name, and so on. To read the port this way, I'm using an ifstream object to open the port as a file, which seems to work well because it's buffered. Then, in another thread, I write the commands to
Using buffered I/O with external devices is a bad idea, IMHO.
This all goes in a library where the listening thread can be started and stopped. I don't want to miss incoming data on the ifstream object, but it seems if I let that thread run on it's own, the other thread to pass on
The thread won't be running on its own if there's no data - it'll go to sleep and other threads will be scheduled.
I know in Perl I can fork and have one thread continue to do it's own thing without it seeming to effect the other. I also know Perl is a much higher
I don't think that Perl's threads are "real" OS-threads.
So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP? (This is going to be for
Well, yes, threads sleep on I/O when I/O is not possible at the given moment.

Zeljko Vrba wrote:
On Mon, Mar 17, 2008 at 11:17:59AM -0400, Hal Vaughan wrote:
thread 2 could start, is that the same? Is there any way I can get the two threads to run concurrently? (I know true multi-tasking on a single CPU is
Yes, you need to properly synchronize between them. Thread A has to explicitly signal thread B to start running (while A puts itself to sleep). If you don't use such signalling, the OS has all right to assume that you don't care about the scheduling order.
So there's nothing I can use to try to split time evenly between them?
just slicing up time, but by concurrently, I mean sharing resources instead of one taking up everything.)
"Sharing resources" is very vaguely defined. If thread A runs for 30 seconds, and then B for 30 seconds without interruption, they will still have received their fair share of resources if the total run time of the program is, say, 1 hour.
This is just a test program, but my big concern is that the program I'm writing does something like this. I'm controlling an HD radio through the serial port (or a usb port with a usb->RS232 adaptor). I know once I mention ports, I'm getting into OS specific stuff, but even beyond that issue, I'm trying to understand what I need to do and what's going on.
title, artist name, and so on. To read the port this way, I'm using an ifstream object to open the port as a file, which seems to work well because it's buffered. Then, in another thread, I write the commands to
Using buffered I/O with external devices is a bad idea, IMHO.
I've had several people comment on that while I've been seeking help on this, but that's the way it seems to work best (as opposed to just opening the serial port and reading it directly) and nobody's told me why it's a bad idea. If the port gets extra data while I'm doing something else, won't it store in the buffer?
This all goes in a library where the listening thread can be started and stopped. I don't want to miss incoming data on the ifstream object, but it seems if I let that thread run on it's own, the other thread to pass on
The thread won't be running on its own if there's no data - it'll go to sleep and other threads will be scheduled.
I know in Perl I can fork and have one thread continue to do it's own thing without it seeming to effect the other. I also know Perl is a much higher
I don't think that Perl's threads are "real" OS-threads.
I don't think so either, but the advantage is that it has some kind of system for managing them. I don't know how complex or simple it is, but I was hoping there might be something more automatic I can use in this case.
So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP? (This is going to be for
Well, yes, threads sleep on I/O when I/O is not possible at the given moment.
That may help, but in the long run, there are people that might be using this in connection with a GUI. How do I make sure my stuff doesn't take up all the resources? Or is that partly the GUI programmer's job? To split things into threads on their end to keep the UI updated? Hal

On Mon, Mar 17, 2008 at 12:18:05PM -0400, Hal Vaughan wrote:
So there's nothing I can use to try to split time evenly between them?
No and yes. Yes, if you use POSIX realtime scheduling and SCHED_RR policy. But this is rather advanced, and if you have a bug in your program, nothing else will get to run, i.e. you will end up with a locked-up machine.
bad idea. If the port gets extra data while I'm doing something else, won't it store in the buffer?
Yes. But the question is -- when the data from the buffer will be delivered to the application. Or vice-versa: when you send something to the stream, when will it be delivered to the device?
That may help, but in the long run, there are people that might be using this in connection with a GUI. How do I make sure my stuff doesn't take up all the resources? Or is that partly the GUI programmer's job? To split
You don't, that's the kernel's job.

Zeljko Vrba wrote:
On Mon, Mar 17, 2008 at 12:18:05PM -0400, Hal Vaughan wrote:
So there's nothing I can use to try to split time evenly between them?
No and yes. Yes, if you use POSIX realtime scheduling and SCHED_RR policy. But this is rather advanced, and if you have a bug in your program, nothing else will get to run, i.e. you will end up with a locked-up machine.
Okay, don't want to get that deep at this point. I'm learning C++ specifically for this project. I've enjoyed doing this more than any coding I've done in 3-4 years since I'm learning new stuff and like the low level abilities C++ gives me, but as far as this project goes, I do want it done without me jumping into something to deep if I can avoid it. I'd rather learn more o my own stuff when I can putter around more.
bad idea. If the port gets extra data while I'm doing something else, won't it store in the buffer?
Yes. But the question is -- when the data from the buffer will be delivered to the application. Or vice-versa: when you send something to the stream, when will it be delivered to the device?
Here's what's odd, and I know this is Linux specific, so I don't know if people want details for info or want me to shut up because it's OT: I open the port twice. I've tested this and it works. I read the data from the port using an ifstream object and to write to it, I use just plain open() and write() to write to the device. It's worked just fine with the RS232 port (but did get funky on the USB port at one point). The data comes in from the port instantly (or so fast I can't tell the difference) and since I'm not using a buffer when I write, it goes in instantly as well. For some reason, though, I can't read from the serial port with just open() and read().
That may help, but in the long run, there are people that might be using this in connection with a GUI. How do I make sure my stuff doesn't take up all the resources? Or is that partly the GUI programmer's job? To split
You don't, that's the kernel's job.
Gotcha! Nice when someone else has to do the ugly work! ;-) Hal

On Mon, Mar 17, 2008 at 04:44:29PM -0400, Hal Vaughan wrote:
port using an ifstream object and to write to it, I use just plain open() and write() to write to the device. It's worked just fine with the RS232
Are you talking about open() and write() methods that belong to stream object, or are you talking about POSIX system calls with the same name?
I'm not using a buffer when I write, it goes in instantly as well. For some reason, though, I can't read from the serial port with just open() and read().
How does this "can't read" manifest? Does open() or read() fail (did you check the errno in that case), does read() never return, does it return with less data than you specified in the argument, ...? Did you set port parameters (parity, stop bits, etc..) correctly? Did you check kernel logs for errors? Did you use strace program to observe in real-time what is actually happening? There are a lot of ways to narrow down "some reason" :-)

On Mon, Mar 17, 2008 at 9:17 AM, Hal Vaughan
So when I did just a loop in a routine that printed numbers 1-10 (or, when I experimented, 1-1000), and thread 1 always finished completely before thread 2 could start, is that the same? Is there any way I can get the two threads to run concurrently?
Either give the threads more work to do (print *all* integers), or sleep for a second between each int.
... I was hoping there might be a way, in C++, to spin off this listener thread, let it keep listening and processing the data, and have it stay in the background so other threads can continue.
You can do this in C++ w/ practically any threading library.
So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP?
Throw in a periodic nanosleep (or whatever) call if the worker thread is doing too much work. This will yield the processor to another thread or process. Jon

Jonathan Franklin wrote:
On Mon, Mar 17, 2008 at 9:17 AM, Hal Vaughan
wrote: So when I did just a loop in a routine that printed numbers 1-10 (or, when I experimented, 1-1000), and thread 1 always finished completely before thread 2 could start, is that the same? Is there any way I can get the two threads to run concurrently?
Either give the threads more work to do (print *all* integers), or sleep for a second between each int.
... I was hoping there might be a way, in C++, to spin off this listener thread, let it keep listening and processing the data, and have it stay in the background so other threads can continue.
You can do this in C++ w/ practically any threading library.
I've tried with pthread and I have to specifically tell it to yield at some point or the other thread doesn't get a chance to do anything until the first thread is done. If you have any resources that can tell me how to do it, I'd really appreciate a few links or other info.
So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP?
Throw in a periodic nanosleep (or whatever) call if the worker thread is doing too much work. This will yield the processor to another thread or process.
That's a help. I hadn't heard of nanosleep at all. (Sometimes you miss a lot when you're teaching yourself!) Thanks! Hal

So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP? (This is going to be for carputers, so it won't be in an SMP environment.)
Now that you've clarified your intended purpose, it is apparent that your simple producer/consumer test program was not a good model of the real program. How the threaded serial port monitor will work in your production environment is something you'll have to experiment with. If the target environment has good support for threads and a fairly high-level driver for the serial port then simply assigning a higher priority to the port monitor thread should work. But it may be necessary to use an interrupt-based solution instead. The normal paradigm for threads on a uniprocessor is that the highest-priority runnable thread runs until it is no longer the highest-priority runnable thread. That can happen when 1) It blocks, either for I/O or on a mutex or 2) The priority of that thread or another thread is changed or 3) Some higher-priority thread becomes runnable (I/O completes, interrupting event occurs, or mutex freed). 4) If more than one thread is runnable at the highest priority, the system may time slice among them at some fairly low frequency (10-1000 ms typically). 5) In non-realtime unix environments, such as the one in which you are experimenting, there is a dynamic priority adjustment scheme at work that is designed to give good interactive responsiveness under load. This scheduler protocol is often not suitable for realtime applications. Time slicing more frequently would impact efficiency because the context switch itself takes time and the new thread will want a different working set in the cache, leading to a lot of unnecessary memory traffic. You may want to pay that price for simulation purposes but in general you don't want it. See others' comments about inserting sleep commands if you want to simulate multiprocessor behavior. Ideally, your production program's port monitor thread would run at high priority but spend most of its time blocked waiting for input. When a frame becomes available the hardware should interrupt the driver, leading to your thread becoming runnable. When the OS returns from the driver's interrupt service routine your input thread should then run until it loops back to wait for more input. If you haven't signaled any semaphores the previously running thread resumes at that point. If you did signal data available a different thread may be scheduled. I've built several systems on this principle very successfully but you have to know your OS and compiler and hardware cold to get reasonable performance and reliability.

Stephen Nuchia wrote:
So is there no way to create a thread and not have it take up all the CPU time until it's done without using SMP? (This is going to be for carputers, so it won't be in an SMP environment.)
Now that you've clarified your intended purpose, it is apparent that your simple producer/consumer test program was not a good model of the real program.
I'm beginning to see that, mainly because, as I read the posts, I'm realizing that there are a lot of low-level issues that I hadn't expected. I'm used to Perl or Java where all that is taken care of. Back in the 1980s I used to program in Assembler and deal with low level issues, but that was on a 6502 (Apple //e), so I never dealt with any issues in terms of threading.
How the threaded serial port monitor will work in your production environment is something you'll have to experiment with. If the target environment has good support for threads and a fairly high-level driver for the serial port then simply assigning a higher priority to the port monitor thread should work. But it may be necessary to use an interrupt-based solution instead.
I was (and still am) hoping it doesn't get to dealing with interrupts. That's a whole different topic and I'm quite new to C++. This kind of programming for me, in any language, is rare. I hardly ever have to deal with the hardware issues. In this case, I didn't realize how important waiting on the serial port could be.
The normal paradigm for threads on a uniprocessor is that the highest-priority runnable thread runs until it is no longer the highest-priority runnable thread. That can happen when 1) It blocks, either for I/O or on a mutex or 2) The priority of that thread or another thread is changed or 3) Some higher-priority thread becomes runnable (I/O completes, interrupting event occurs, or mutex freed). 4) If more than one thread is runnable at the highest priority, the system may time slice among them at some fairly low frequency (10-1000 ms typically). 5) In non-realtime unix environments, such as the one in which you are experimenting, there is a dynamic priority adjustment scheme at work that is designed to give good interactive responsiveness under load. This scheduler protocol is often not suitable for realtime applications.
By non-real time, you mean the demo program, right?
Time slicing more frequently would impact efficiency because the context switch itself takes time and the new thread will want a different working set in the cache, leading to a lot of unnecessary memory traffic. You may want to pay that price for simulation purposes but in general you don't want it. See others' comments about inserting sleep commands if you want to simulate multiprocessor behavior.
After looking over what I've learned (and thanks to all, because this thread on threads has been an education in itself), I don't think the threads and timing will be an issue because the one reading from the serial port will be blocked most of the time (it never seems to get more than 256 bytes at a time and rarely more than 40 at a time) and when it isn't, I can use nanosleep as it processes to make sure it never hogs resources. That should make that thread almost invisible in terms of the time it consumes. (Is my reasoning right on that?)
Ideally, your production program's port monitor thread would run at high priority but spend most of its time blocked waiting for input. When a frame becomes available the hardware should interrupt the driver, leading to your thread becoming runnable. When the OS returns from the driver's interrupt service routine your input thread should then run until it loops back to wait for more input. If you haven't signaled any semaphores the previously running thread resumes at that point. If you did signal data available a different thread may be scheduled.
I don't need to signal data available, at least as far as I know. I may by simply changing a boolean flag.
I've built several systems on this principle very successfully but you have to know your OS and compiler and hardware cold to get reasonable performance and reliability.
That's the hard part. This is working at a low level I haven't worked at for about 20 years and when I did, there was no multi-tasking and no issues like this. (Sometimes I still year for days when I was working on simpler 8 bit systems!) Hal

Hal Vaughan:
I'm beginning to see that, mainly because, as I read the posts, I'm realizing that there are a lot of low-level issues that I hadn't expected. I'm used to Perl or Java where all that is taken care of.
You should be able to replicate the results of your test in Java and even Perl using processes. The issues aren't language-dependent.

Peter Dimov wrote:
Hal Vaughan:
I'm beginning to see that, mainly because, as I read the posts, I'm realizing that there are a lot of low-level issues that I hadn't expected. I'm used to Perl or Java where all that is taken care of.
You should be able to replicate the results of your test in Java and even Perl using processes. The issues aren't language-dependent.
I get different results in Perl. For example if I do two threads, each with a loop printing out the numbers 1-100 (or even 1,000) in Perl, the output is interspersed. Thread 1 prints one or more, Thread 2 prints one or more and so on. If I do it in C++, Thread 1 prints them all, then Thread 2 prints them all. That's what I mean by it being different at this level. The Perl interpreter is handling switching between the different Perl threads. Hal

Hal Vaughan:
Peter Dimov wrote:
Hal Vaughan:
I'm beginning to see that, mainly because, as I read the posts, I'm realizing that there are a lot of low-level issues that I hadn't expected. I'm used to Perl or Java where all that is taken care of.
You should be able to replicate the results of your test in Java and even Perl using processes. The issues aren't language-dependent.
I get different results in Perl. For example if I do two threads, each with a loop printing out the numbers 1-100 (or even 1,000) in Perl, the output is interspersed.
That's why I added the "using processes" part. :-)

Try those links I sent or check your favorite vendor site. Most of these guys publish educational material to get people to use their products in the best possible way. I happen to be familiar with Intel but there may be better ones. [ the formating below is because hotmail now thinks that I want to send graphics, not information. LOL ] Mike Marchywka 586 Saint James Walk Marietta GA 30067-7165 404-788-1216 (C)<- leave message 989-348-4796 (P)<- emergency only marchywka@hotmail.com Note: If I am asking for free stuff, I normally use for hobby/non-profit information but may use in investment forums, public and private. Please indicate any concerns if applicable. Note: Hotmail is possibly blocking my mom's entire ISP - try me on marchywka@yahoo.com if no reply here. Thanks. _________________________________________________________________ Connect and share in new ways with Windows Live. http://www.windowslive.com/share.html?ocid=TXT_TAGHM_Wave2_sharelife_012008

I don't have a specific reference and I am not pushing any particular company but Intel has developed some good online resources to help people use their hyperthreading product. They have a lot of pdf files but I guess they want you to buy their intro book, http://softwarecommunity.intel.com/articles/eng/1567.htm Rule 6 here may answer your immediate question, http://softwarecommunity.intel.com/articles/eng/1607.htm I also saw something on google about FFTW that mentions multi-threading. IF you are interested in efficient numerical stuff more generally, their site may be worth some time. Mike Marchywka 586 Saint James Walk Marietta GA 30067-7165 404-788-1216 (C)<- leave message 989-348-4796 (P)<- emergency only marchywka@hotmail.com Note: If I am asking for free stuff, I normally use for hobby/non-profit information but may use in investment forums, public and private. Please indicate any concerns if applicable. Note: Hotmail is possibly blocking my mom's entire ISP - try me on marchywka@yahoo.com if no reply here. Thanks.
From: hal@thresholddigital.com To: boost-users@lists.boost.org Date: Mon, 17 Mar 2008 09:42:46 -0400 Subject: [Boost-users] Threads Don't Take Turns Under Linux (Ubuntu Gutsy Gibbon)
I apologize if this gets posted more than once. I found the group on GMane, tried to post, joined the email list, then tried to post several times but still have yet to see it show up.
I'm learning C++ and found a tutorial on threads with Boost on Dr. Dobb's site. I've copied the code from this url:
http://www.ddj.com/cpp/184401518?pgno=5
It's supposed to fill a buffer with data on one thread and remove data on the other thread. I would expect it to add data and remove at almost the same time but when I run it, it adds 10 items, then removes them, then adds ten again, and so on. It's never adding and removing in an "interleaved" fashion. I've also tried the same with simple counters where both threads count to ten, but it doesn't seem to make a difference. It seems no matter how I set it up, one thread either completes, or runs until it has to stop for a mutex, then the other thread runs. It seems that there is never a case of both threads running at the same time. I've never had trouble doing this in Perl with fork() so I'm not at all sure what could be going wrong.
As I said, it's not just the program below, it seems to be any program that uses more than one thread.
Am I doing something wrong or is there a setting I need to change?
I'm compiling it with "g++ -lboost_thread mthrd.cpp -o mthrd && ./mthrd";
Thanks!
Hal ------ #include #include #include #include
const int BUF_SIZE = 10; const int ITERS = 100;
boost::mutex io_mutex;
class buffer { public: typedef boost::mutex::scoped_lock scoped_lock;
buffer() : p(0), c(0), full(0) { }
void put(int m) { scoped_lock lock(mutex); if (full == BUF_SIZE) { { boost::mutex::scoped_lock lock(io_mutex); std::cout << "Buffer is full. Waiting..." << std::endl; } while (full == BUF_SIZE) cond.wait(lock); } buf[p] = m; p = (p+1) % BUF_SIZE; ++full; cond.notify_one(); }
int get() { scoped_lock lk(mutex); if (full == 0) { { boost::mutex::scoped_lock lock(io_mutex); std::cout << "Buffer is empty. Waiting..." << std::endl; } while (full == 0) cond.wait(lk); } int i = buf[c]; c = (c+1) % BUF_SIZE; --full; cond.notify_one(); return i; }
private: boost::mutex mutex; boost::condition cond; unsigned int p, c, full; int buf[BUF_SIZE]; };
buffer buf;
void writer() { for (int n = 0; n < ITERS; ++n) { { boost::mutex::scoped_lock lock(io_mutex); std::cout << "sending: " << n << std::endl; } buf.put(n); } }
void reader() { for (int x = 0; x < ITERS; ++x) { int n = buf.get(); { boost::mutex::scoped_lock lock(io_mutex); std::cout << "received: " << n << std::endl; } } }
int main(int argc, char* argv[]) { boost::thread thrd1(&reader); boost::thread thrd2(&writer); thrd1.join(); thrd2.join(); return 0; } _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_________________________________________________________________ Climb to the top of the charts! Play the word scramble challenge with star power. http://club.live.com/star_shuffle.aspx?icid=starshuffle_wlmailtextlink_jan

Hal Vaughan: ...
It's supposed to fill a buffer with data on one thread and remove data on the other thread. I would expect it to add data and remove at almost the same time but when I run it, it adds 10 items, then removes them, then adds ten again, and so on. It's never adding and removing in an "interleaved" fashion. I've also tried the same with simple counters where both threads count to ten, but it doesn't seem to make a difference. It seems no matter how I set it up, one thread either completes, or runs until it has to stop for a mutex, then the other thread runs. It seems that there is never a case of both threads running at the same time. I've never had trouble doing this in Perl with fork() so I'm not at all sure what could be going wrong.
There are several possible answers to this one, from worst to best: A1. Unlock the mutex before notify_one and then yield. A2. The threads are typically scheduled to run for a certain timesice; 10ms is one possible value. A context switch from one thread to another takes time, and the fact that the new thread doesn't have its data in the cache is an additional performance hit. Context switches are pure waste of time, so the less context switches your program performs, the faster it will appear to run. In your case, the pushing thread can do many iterations in its timeslice, so it runs until the buffer is full, then blocks. A3. In a "real" multithreaded program, you almost never want threads to run interleaved. You want them to run at the same time on different CPUs in order to achieve maximum throughput. Amdahl's law says that the time it takes for a program on K CPUs to finish is S + P / k, where S is the sequential portion and P is the parallelizable portion. The less S you have, the better. It follows that benchmarks that have P == 0, such as one in which the threads repeatedly fight for the same mutex, are generally useless; they would take much less time if rewritten to run single-threaded. You want your benchmark to measure something significant; for that you need something along the lines of: void writer() { for (int n = 0; n < ITERS; ++n) { // create an item "it", taking time buf.put( it ); } } void reader() { for (int x = 0; x < ITERS; ++x) { Item it = buf.get(); // process the item "it", taking time } } where the time it takes to create an item and to process an item is significant compared to the queue overhead (or a single thread creating and processing items will be faster). I see in your later post that an item is "created" by reading from a serial port. In this case, if you use blocking reads, you won't need to do anything; the "writer" thread will just block until there's something to read from the port, and the "reader" thread will block when there's no data in the queue.
participants (8)
-
Bob Warren
-
Hal Vaughan
-
Hughes, James
-
Jonathan Franklin
-
Mike Marchywka
-
Peter Dimov
-
Stephen Nuchia
-
Zeljko Vrba