Introduction
In this article, I will show you how to run an infinite loop without frying the CPU in C.
In Go it’s easy to achieve this by using channels and goroutines.
I am new to C and I wanted to see how I can achieve the same thing in C.
So if you are an experienced C developer, please let me know if there is a better way to do this.
First download the source code from here demo.zip
The Problem
let’s say we have the following scenario, where I need to wait for someone to push a work before I can process it.
the trivial method is to create an infinite loop and check if we can process the work.
void Process() {
for (;;) {
while(atomic_load(&pushed_count) == 0) {
printf("nothing to process\n");
}
printf("Processor: Doing some work...\n");
atomic_fetch_add(&processed_count, 1);
atomic_fetch_sub(&pushed_count, 1);
}
}
that is my processing function, it will keep checking if there is work to process, if there is it will process it.
the problem with this code is that it will keep the CPU busy and will consume a lot of power.
to verify that, let’s run the code and see how much CPU it consumes, we can use any tool like htop
or top
to see the CPU usage.
./build.sh
./demo1
to monitor CPU usage I will run in a split
top -p $(pgrep demo1)
in the image we can see that CPU usage is 99.7% and the process is consuming a lot of power.
The Solution
Of course, we can use a sleep function to give the CPU a break, but this is not a good solution because we don’t know how long we should sleep.
So we need a way to signal the processor to process the work.
The solution is to use pthread_cond_wait
to wait for a signal to process the work.pthread_cond_wait
will block the thread until a signal is received.
void Process() {
for (;;) {
printf("Processor: Waiting for a signal...\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
printf("Processor: Signal received\n");
printf("Processor: Doing some work...\n");
atomic_fetch_add(&processed_count, 1);
atomic_fetch_sub(&pushed_count, 1);
}
}
here the process will wait for a signal to process the work, and it will not consume a lot of power.
in the pusher code we need to send a signal to the processor to process the work.
printf("Thread %ld is pushing...\n", thread_index);
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
atomic_fetch_add(&pushed_count, 1);
to verify that, let’s run the code and see how much CPU it consumes.
./demo2
and in a split terminal
top -p $(pgrep demo2)
and we can see that the CPU usage is 0.0% and the process is not consuming a lot of power.
Conclusion
As I said earlier, I am new to C and I wanted to see how I can achieve the same thing in C.
So if you are an experienced C developer, please let me know if there is a better way to do this.
It’s always good to learn new things and see how things work under the hood.
This can be very useful in many scenarios where you need to give the CPU a break.
Also there are many improvements to do in this code of course and use memory barriers to make sure that the data is consistent across threads.