Threads Programming in Linux: Examples
One of the important purpose of threads is to achieve concurrency. There may be many independent tasks in a program which can be done in parallel without the influence of the other. One of the first step in using threads is to first recognize the fact whether the program needs threads or not, otherwise the very purpose of threads becomes futile. For example if you are designing a program which involves reading a file and display it, you may utilize two or more threads, one to read the file, the second to update the display and other threads to monitor the user inputs in the form of keyboard interrupts or mouse movements. There can be cases when threads are dependent on each other like in case of our above example, if the concerned application is an editor so a character key pressed should be soon informed to the other thread which updates display so that it may open the menu corresponding to that key shortcut or do some other appropriate action.
Example 1:
Two threads displaying two strings “Hello” and “How are you?” independent of each other.
#include #include #include void * thread1() < while(1)< printf("Hello!!\n"); >> void * thread2() < while(1)< printf("How are you?\n"); >> int main()
Now compile this program (Note the -l option is to load the pthread library)
On running, you can see many interleaved “Hello!!” and “How are you?” messages
Example 2
This example involves a reader and a writer thread. The reader thread reads a string from the user and writer thread displays it. This program uses semaphore so as to achieve synchronization
#include #include #include #include char n[1024]; sem_t len; void * read1() < while(1)< printf("Enter a string"); scanf("%s",n); sem_post(&len); >> void * write1() < while(1)< sem_wait(&len); printf("The string entered is :"); printf("==== %s\n",n); >> int main()
On running, in most cases we may be able to achieve a serial read and write( Thread1reads a string and Thread2 displays the same string). But suppose we insert a sleep function() in write1 like
The thread 1 may read one more string and thread2 displays the last read string. That is no serial read and write is achieved.
So we may need to use the condition variables to achieve serial read and write.
Example 3
This example involves a reader and a writer thread. The reader thread reads a string from the user and writer thread displays it. This program uses condition variables to achieve synchronization and achieve serial programming .
#include #include #include #include #define TRUE 1 #define FALSE 0 char n[1024]; pthread_mutex_t lock= PTHREAD_MUTEX_INITIALIZER; int string_read=FALSE; pthread_cond_t cond; void * read1() < while(1)< while(string_read); pthread_mutex_lock(&lock); printf("Enter a string: "); scanf("%s",n); string_read=TRUE; pthread_mutex_unlock(&lock); pthread_cond_signal(&cond); >> void * write1() < while(1)< pthread_mutex_lock(&lock); while(!string_read) pthread_cond_wait(&cond,&lock); printf("The string entered is %s\n",n); string_read=FALSE; pthread_mutex_unlock(&lock); >> int main()
The output is serial read and write.
In the beginning, I started the discussion that threads are used to achieve concurrency. But the above examples can be easily done by simple scanf and printf i.e.,
scanf (“%s”,n);
printf (“%s”,n);
But these examples were given to demonstrate the semaphores and condition variables. Example 3 can be further modified to design a reader/writer application. Example string_read boolean variable can be converted to a string_count variable.
Программирование C в Linux — потоки pthreads
Многопоточность в программировании является важным механизмом в наше время. Поэтому я решил посвятить несколько статей этой теме.
В семействах ОС Windows — каждая программа запускает один процесс выполнения, в котором находится как минимум один поток (нить). В процессе может находиться множество потоков, между которыми делится процессорное время. Один процесс не может напрямую обратиться к памяти другого процесса, а потоки же разделяют одно адресное пространство одного процесса. То есть в Windows — процесс это совокупность потоков.
В Linux же немного по-другому. Сущность процесса такая же, как и в Windows — это исполняемая программа со своими данными. Но вот поток в Linux является отдельным процессом (можно встретить название как «легковесный процесс», LWP). Различие такое же — процесс отдельная программа со своей памятью, не может напрямую обратиться к памяти другого процесса, а вот поток, хоть и отдельный процесс, имеет доступ к памяти процесса-родителя [2]. LWP процессы создаются с помощью системного вызова clone() с указанием определенных флагов.
Но также имеется такая вещь, которая называется «POSIX Threads» — библиотечка стандарта POSIX, которая организует потоки (они же нити) внутри процесса. Т.е тут уже распараллеливание происходит в рамках одного процесса.
И тут встает вопрос различия терминов «поток», «процесс», «нить» и т.д. Проблема в том, что в англоязычной литературе данные термины определяются однозначно, у нас же с нашим великим и могучим имеются противоречия, что может привести к дикому диссонансу.
Но это все в общих чертах, для более точной информации следует обратиться к соответствующей литературе, либо к официальной документации, можно почитать man’ы. В конце статьи я приведу несколько полезных ссылок на ресурсы, где более подробно расписано как все работает, а пока займемся практикой.
Я рассмотрю два варианта «распараллеливания» программы — создания потока/нити с помощью функций из pthread.h (POSIX Threads), либо создание отдельного процесса с помощью функции fork().
Сегодня рассмотрим потоки из библиотеки pthread.
Шаблон кода для работы с потоками выглядит следующим образом: