User Tools

Site Tools


os_cp:threads

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
os_cp:threads [2023/05/23 23:21]
jkonczak
os_cp:threads [2024/04/18 11:32]
jkonczak [Accessing the same data from multiple threads]
Line 52: Line 52:
 pointer to arbitrary data((To specify a pointer without specifying the type of pointer to arbitrary data((To specify a pointer without specifying the type of
 underlying data one has to use ''​void *''​.)):​ underlying data one has to use ''​void *''​.)):​
 +<​html><​div style="​margin-top:​-1.4em;​line-height:​1.2em"></​html>​
 <code c> <code c>
 void * start_routine (void * argument){ void * start_routine (void * argument){
Line 58: Line 59:
 } }
 </​code>​ </​code>​
 +<​html></​div></​html>​
 <​small>​In C such function is of type ''​void *(*)(void *)'',​ and a variable called <​small>​In C such function is of type ''​void *(*)(void *)'',​ and a variable called
 ''​func''​ of this type should be declared as ''​void *(*func)(void *)''​.</​small>​ ''​func''​ of this type should be declared as ''​void *(*func)(void *)''​.</​small>​
Line 77: Line 79:
 argument is not NULL, thread attributes will be set before starting the thread. argument is not NULL, thread attributes will be set before starting the thread.
  
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 ++++An example of creating a thread and passing it some data| ++++An example of creating a thread and passing it some data|
 <code c> <code c>
Line 138: Line 140:
 The ''//​value_ptr//''​ can be NULL, and then the return value is discarded. The ''//​value_ptr//''​ can be NULL, and then the return value is discarded.
  
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 ++++An example code that creates 3 threads and joins them, collecting their result.| ++++An example code that creates 3 threads and joins them, collecting their result.|
 <code c> <code c>
Line 217: Line 219:
 typically the thread that spawned it calls the detach. typically the thread that spawned it calls the detach.
  
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 ++++An example code that creates a thread and detaches it.| ++++An example code that creates a thread and detaches it.|
 <code c> <code c>
Line 297: Line 299:
  
 ~~Exercise.#​~~ ~~Exercise.#​~~
-Remove the code you wrote for the previous exercise the ''​pthread_join'' ​and +Remove ​from the code you wrote for the previous exercise the ''​pthread_join''​ 
-re-run the code. What has changed in the behaviour of the program?+and re-run the code. What has changed in the behaviour of the program?
  
 ~~Exercise.#​~~ ~~Exercise.#​~~
Line 312: Line 314:
 In the main thread, collect the returned numbers and display them. In the main thread, collect the returned numbers and display them.
  
-===== Thread-specific data, thread local storage [extra] ===== 
  
-In C, data items can have different storage duration and different linkage. +===== Accessing the same data from multiple threads =====
-Until C11, there existed only static, automatic or allocated storage durations. +
-Roughly: +
-\\ +
- • static ​global variables and static variables defined inside functions,​ +
-\\ +
- • automatic ​items on stack, automatically allocated inside functions ('​local variables'​),​ +
-\\ +
- • allocated ​items on heap, explicitly allocated and freed. +
-\\ +
-None of these cover a case when one wants a static ("​global"​) variable with +
-a separate value for each thread. +
-Such storage duration is called [[https://​en.wikipedia.org/​wiki/​Thread-local_storage|thread local]] +
-and a coupe of ways to create thread-local items are presented in this section.+
  
-==== POSIX Thread-specific ​data ====+The following examples show what might happen upon accessing the same data from 
 +multiple threads with no synchronisation.
  
-One can create //keys// (that may be understood as identifiers of variables) and +Firstlet's read/write "linearly" ​from/to an array from two threads
-then associatefor each thread separately, a value for a key. +<​html><​div style="​margin-top:​-1.4em;​line-height:​1.2em"></​html>​
-To this end the following functions can be used: +
-\\ +
-<​html><​span style="​float:​right"><​small>​Need header:<​br><​code>​pthread.h<​/code></​small></​span>​ +
-<a href="https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_key_create.html"></html> +
-''​int **pthread_key_create**(pthread_key_t *//key//, void (*//​destructor//​)(void*))''​ +
-<​html></​a></​html>​ +
-\\ +
-<​html><​a href="​https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_setspecific.html"></​html>​ +
-''​int **pthread_setspecific**(pthread_key_t //key//, const void *//​value//​)''​ +
-\\ +
-''​void %%*%%**pthread_getspecific**(pthread_key_t //​key//​)''​ +
-<​html></​a></​html>​ +
-\\ +
-Each key is initially associated with a ''​NULL''​ for each thread. +
- +
-<​html><​div style="​line-height:​1em"></​html>​ +
-++++An example code that creates a key, stores and retrieves the value from two threads.|+
 <code c> <code c>
 #include <​pthread.h>​ #include <​pthread.h>​
 #include <​stdio.h>​ #include <​stdio.h>​
-#include <​stdlib.h>​ 
 #include <​string.h>​ #include <​string.h>​
  
-pthread_key_t mySpecificVal;+unsigned long long version; 
 +char text[1020];
  
-void printMine() { +void *updater(void * arg) { 
-  ​printf("I have: %s\n", ​(char *)pthread_getspecific(mySpecificVal));+  ​while (1) 
 +    for (char letter = '​a';​ letter <= '​z';​ ++letter
 +      version++;​ 
 +      for (int i = 0; i < 1020 - 1; ++i) 
 +        text[i] = letter; 
 +      // memset(text,​ letter, 1020); 
 +    }
 } }
  
-void *routine(void *arg) { +int main() { 
-  ​char *text = malloc(4)+  ​pthread_t tid
-  ​strcpy(text"​baz"​); +  ​pthread_create(&tidNULL, updater, NULL); 
-  ​pthread_setspecific(mySpecificVal,​ text); +  ​while (getchar() != EOF) 
-  ​printMine(); +    ​printf("​version:​ %llu\n ​  text: %s\n", version, text); 
-  return ​NULL;+  return ​0;
 } }
 +</​code>​
 +<​html></​div></​html>​
  
-int main() { +Next, let's read-modify-write the same variable from multiple threads: 
-  char *text malloc(4);​ +<​html><​div style="margin-top:​-1.4em;line-height:​1.2em"></​html>​ 
-  strcpy(text, ​"foo");+<code c> 
 +#include <​pthread.h>​ 
 +#include <​stdio.h>​
  
-  pthread_key_create(&​mySpecificVal,​ free); +unsigned long long counter;
-  pthread_setspecific(mySpecificVal,​ text);+
  
-  pthread_t ti; +void *incrementer(void * arg) { 
-  ​pthread_create(&ti, NULL, routine, NULL); +  ​for (int i = 0; i < 1000; ++i) 
-  ​pthread_join(ti, ​NULL);+    counter++
 +  ​return ​NULL; 
 +}
  
-  printMine();+int main() { 
 +  pthread_t tid[16]; 
 +  for (int i = 0; i < 16; ++i) 
 +    pthread_create(tid + i, NULL, incrementer,​ NULL); 
 +  for (int i = 0; i < 16; ++i) 
 +    pthread_join(tid[i],​ NULL); 
 +  printf("​%llu\n",​ counter);
   return 0;   return 0;
 } }
 </​code>​ </​code>​
-++++ 
 <​html></​div></​html>​ <​html></​div></​html>​
  
-==== C thread-local storage ==== +~~Exercise.#~~ 
- +Readunderstand ​the code, compile and run the two above examples. 
-Starting with C11, a new storage duration ''​_Thread_local''​ or +What is wrong with the results? What problem do these examples show?
-''​[[https://​en.cppreference.com/​w/​c/​thread/​thread_local|thread_local]]''​((Until +
-C23 ''​thread_local''​ was a macro defined in ''​threads.h''​since C23 it +
-is a keyword with same meaning as the ''​_Thread_local''​ keyword.)) is defined. +
-Variables defined ​with this storage duration have unique value for each thread. +
-Notice that the ''​thread_local''​ variables can have initial values (unlike in +
-POSIX), but there is no way to provide a custom destructor (unlike in POSIX). ​+
  
 ===== POSIX mutexes and readers-writers locks ===== ===== POSIX mutexes and readers-writers locks =====
- 
-==== Mutex vs binary semaphore ==== 
- 
-There are two vital differences between mutexes and semaphores: 
-  - A mutex locked by some thread //belongs// to it and only the thread is allowed to unlock it.\\ A semaphore can be decremented/​incremented by distinct threads/​processes. 
-  - A mutex can be configured recursive, and then it can be locked multiple times by the same thread and will be unlocked only after corresponding number of unlocks. 
  
 ==== Mutexes ==== ==== Mutexes ====
Line 457: Line 433:
 Some mutex types also guarantee failing((returning ''​-1''​ from ''​pthread_mutex_unlock''​ and setting ''​errno''​ accordingly.)) to unlock a lock not owned by the current thread – all mutexes of ''​PTHREAD_MUTEX_ERRORCHECK''​ and ''​PTHREAD_MUTEX_RECURSIVE''​ do that, as well as any mutex set to be robust does that. Some mutex types also guarantee failing((returning ''​-1''​ from ''​pthread_mutex_unlock''​ and setting ''​errno''​ accordingly.)) to unlock a lock not owned by the current thread – all mutexes of ''​PTHREAD_MUTEX_ERRORCHECK''​ and ''​PTHREAD_MUTEX_RECURSIVE''​ do that, as well as any mutex set to be robust does that.
  
-Example ​of creating a recursive mutex:+An example ​of creating a recursive mutex is as follows: 
 +<​html><​div style="​margin-top:​-1.4em;​line-height:​1.2em"></​html>​
 <code c> <code c>
 pthread_mutexattr_t recursiveAttrs;​ pthread_mutexattr_t recursiveAttrs;​
Line 465: Line 442:
 pthread_mutex_init(&​mutex,​ &​recursiveAttrs);​ pthread_mutex_init(&​mutex,​ &​recursiveAttrs);​
 </​code>​ </​code>​
 +<​html></​div></​html>​
  
 To lock a mutex one can use either of: To lock a mutex one can use either of:
Line 496: Line 474:
  
 ~~Exercise.#​~~ ~~Exercise.#​~~
-Use a mutex to fix the program below so that every ''​printf''​ outputs 999 +Use a mutex to fix the programs from the two examples in the section 
-identical letters. +[[#​accessing_the_same_data_from_multiple_threads|accessing the same data from multiple threads]].
-<​html><​div style="​line-height:​1em"></​html>​ +
-<code c letters.c>​ +
-#include <​pthread.h>​ +
-#include <​stdio.h>​ +
- +
-char buffer[1000]; +
- +
-void *writer(void *arg) { +
-  while (1) +
-    for (char letter = '​a';​ letter <= '​z';​ ++letter) +
-      for (int i = 0; i < 999; ++i) +
-        buffer[i= letter; +
-+
- +
-int main() { +
-  pthread_t thread; +
-  pthread_create(&​thread,​ NULL, writer, NULL); +
-  pthread_detach(thread);​ +
- +
-  while (getchar() != EOF) +
-    printf("​%s\n",​ buffer); +
- +
-  return 0; +
-+
-</​code>​ +
-<​html></​div></​html>​+
  
 <​small>​ <​small>​
 +<​html><​div style="​margin-bottom:​-1.2em"></​html>​
 ~~Exercise.#​~~ ~~Exercise.#​~~
 The program below accesses a linked list from multiple threads. The program below accesses a linked list from multiple threads.
 Add mutexes to functions which names start with ''​list_…''​ so that the program Add mutexes to functions which names start with ''​list_…''​ so that the program
 no longer crashes. no longer crashes.
-<​html><​div style="​line-height:​1em"></​html>​+<​html></​div></​html>​ 
 +++++ Source code for this exercise: | 
 +<​html><​div style="​line-height:​1.2em"></​html>​
 <code c linkedlist.c>​ <code c linkedlist.c>​
 #include <​pthread.h>​ #include <​pthread.h>​
Line 627: Line 582:
 </​code>​ </​code>​
 <​html></​div></​html>​ <​html></​div></​html>​
 +++++
  
 ~~Exercise.#​~~ ~~Exercise.#​~~
 Add to the program from the previous exercise a thread executing the following Add to the program from the previous exercise a thread executing the following
 function, and amend the code so that it neither crashes nor deadlocks: function, and amend the code so that it neither crashes nor deadlocks:
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 <code c linkedlist.c>​ <code c linkedlist.c>​
 void *beheader(void *arg) { void *beheader(void *arg) {
Line 665: Line 621:
 \\ \\
 To unlock a rwlock (locked in any mode), ''​[[https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_rwlock_unlock.html|pthread_rwlock_unlock]]''​ is used. To unlock a rwlock (locked in any mode), ''​[[https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_rwlock_unlock.html|pthread_rwlock_unlock]]''​ is used.
 +
 +
 +===== [extra] Concurrency,​ parallelism =====
 +
 +The aim of __[[https://​en.wikipedia.org/​wiki/​Parallel_computing|parallel programming]]__
 +is to take advantage of the fact that a computer can execute two or more sequences
 +of instructions at the same time (literally).
 +\\
 +One needs at least two physical threads (for instance, two CPU cores) for this.
 +
 +__[[https://​en.wikipedia.org/​wiki/​Resource_contention|Contention]]__ is the situation
 +when two processes / threads want to use the same resource (say, the same variable)
 +at the same time.
 +
 +__Concurrent programming__ deals with processes / threads that contend on some
 +resources (and run either interleaved or in parallel).
 +\\
 +Concurrency also concerns computers that have one physical thread (a single CPU
 +core) whenever they can switch context from one process / thread to another.
 +
 +When two processes / threads access the same data and at least one of the
 +accesses modifies the data, then the __[[https://​en.wikipedia.org/​wiki/​Race_condition#​In_software|race condition]]__
 +occurs – the result of the computation depends upon a chance.
 +\\
 +Race conditions often lead to incorrect state of the data and thus introduce
 +bugs (that are usually hard to pinpoint).
 +
 +Regions of code that must be executed without being interleaved among each other
 +are called __[[https://​en.wikipedia.org/​wiki/​Critical_section|critical sections]]__.
 +At most one process may be at a time inside inside a critical section.
 +
 +When a process P wants to enter a critical section, and another process Q is
 +executing a critical section, then P must __wait__ (aka __block__) until Q exits
 +the critical section.
 +
 +When a number of processes / threads are waiting on synchronisation primitives
 +(say, blocked by ''​pthread_mutex_lock''​),​ and the only processes / threads that
 +could wake the ones waiting (say, execute ''​pthread_mutex_unlock''​) are within
 +the waiting ones, then a __[[https://​en.wikipedia.org/​wiki/​Deadlock|deadlock]]__
 +occurred.
 +\\
 +A deadlock usually means that the processing stops.
 +
 +<​html><​div style="​margin-bottom:​-1em"></​html>​
 +It is also possible to run into a state when processes don't stop, but can't
 +progress either. This is called a __livelock__. Imagine the following code:
 +<​html></​div></​html>​
 +<​html><​div style="​line-height:​1em"></​html>​
 +<​small>​
 +<code c>
 +/* x, y and mtx are shared */
 +/* the programmer believed that eventually x equals y, but now x==1, y==0 and only those two processes run: */
 +/* Thread 1 */                │    /* Thread 2 */
 +char loop = 1;                │    char loop = 1;
 +while(loop) {                 ​│ ​   while(loop) {
 +  pthread_mutex_lock(&​mtx); ​  ​│ ​     pthread_mutex_lock(&​mtx);​
 +  if (*x==0 && *y==0) {       ​│ ​     if (*x==1 && *y==1) {
 +    /* do business logic */   ​│ ​       /* do business logic */
 +    loop = 0;                 ​│ ​       loop = 0;
 +  }                           ​│ ​     }
 +  pthread_mutex_unlock(&​mtx);​ │      pthread_mutex_unlock(&​mtx);​
 +}                             ​│ ​   }
 +</​code>​
 +</​small>​
 +<​html></​div></​html>​
 +
 +__Fairness__ describes whether all processes / threads have equal chance to enter
 +the critical section. ​
 +Sometimes priorities are intentionally given to selected processes / threads.
 +Incorrect implementation of priorities may lead to 
 +__[[https://​en.wikipedia.org/​wiki/​Priority_inversion|priority inversion]]__.
 +\\
 +If the algorithm is completely unfair for some process / thread, it can
 +__[[https://​en.wikipedia.org/​wiki/​Starvation_(computer_science)|starve]]__ by
 +never entering the critical section on contention.
 +
 +===== Critical sections, deadlocks =====
 +
 +~~Exercise.#​~~ Which lines of code are the critical section in the code below?
 +
 +~~Exercise.#​~~ Spot the deadlock scenario in the code below.
 +
 +~~Exercise.#​~~ Fix the code so that it no longer is able to deadlock.
 +
 +<​html><​div style="​line-height:​1.2em"></​html>​
 +<​small>​
 +<code c another_bad_idea.c>​
 +#include <​pthread.h>​
 +#include <​stdint.h>​
 +#include <​stdio.h>​
 +#include <​stdlib.h>​
 +#include <​string.h>​
 +
 +struct {
 +  pthread_mutex_t mtx;
 +  char text[256];
 +} item[5];
 +
 +const char *arg0;
 +
 +void *threadFunc(intptr_t num) {
 +  char name[1024], cmd[1024];
 +  sprintf(name,​ "​%s.pipe.%ld",​ arg0, num);
 +  sprintf(cmd,​ "rm -f %s; mkfifo %s", name, name);
 +  system(cmd);​
 +  FILE *myPipe = fopen(name, "​r+"​);​
 +
 +  while (1) {
 +    char line[1024], nl;
 +    fscanf(myPipe,​ "​%1023[^\n]%c",​ line, &nl);
 +
 +    int argOne = atoi(line);
 +    if (argOne >= 5 || argOne < 0)
 +      continue;
 +
 +    char *argTwoTxt = strchr(line,​ ' ');
 +
 +    if (!argTwoTxt) {
 +      pthread_mutex_lock(&​item[argOne].mtx);​
 +      printf("​T%ld reads %d as: %s\n", num, argOne, item[argOne].text);​
 +      pthread_mutex_unlock(&​item[argOne].mtx);​
 +      continue;
 +    }
 +
 +    argTwoTxt++;​
 +    char *e;
 +    int argTwo = strtol(argTwoTxt,​ &e, 10);
 +
 +    if (!*e && argTwo < 5 && argTwo >= 0 && argOne != argTwo) {
 +      pthread_mutex_lock(&​item[argOne].mtx);​
 +      pthread_mutex_lock(&​item[argTwo].mtx);​
 +      printf("​T%ld copies %d to %d\n", num, argTwo, argOne);
 +      memcpy(item[argOne].text,​ item[argTwo].text,​ sizeof(item[argOne].text));​
 +      pthread_mutex_unlock(&​item[argTwo].mtx);​
 +      pthread_mutex_unlock(&​item[argOne].mtx);​
 +    } else {
 +      pthread_mutex_lock(&​item[argOne].mtx);​
 +      printf("​T%ld assigns to %d the value: %s\n", num, argOne, argTwoTxt);
 +      memset(item[argOne].text,​ 0, sizeof(item[argOne].text));​
 +      strncpy(item[argOne].text,​ argTwoTxt, sizeof(item[argOne].text) - 1);
 +      pthread_mutex_unlock(&​item[argOne].mtx);​
 +    }
 +  }
 +}
 +
 +int main(int argc, char **argv) {
 +  arg0 = argv[0];
 +
 +  printf("​To use this program, write to one of the %s.pipe.<​thread_id>​ the "
 +         "​following:​\n"​
 +         " ​ <​num> ​            ​prints <​text>​ from item <​num>​\n"​
 +         " ​ <num> <​text> ​     puts <​text>​ to item <​num>​\n"​
 +         " ​ <​num1>​ <​num2> ​    ​copies to item <​num1>​ the text from item <​num2>​\n"​
 +         "​Valid pipe numbers are 0-4, valid item numbers are 0-4.",
 +         ​arg0);​
 +
 +  for (int i = 0; i < 5; ++i)
 +    pthread_mutex_init(&​item[i].mtx,​ NULL);
 +
 +  for (intptr_t i = 1; i < 5; ++i) {
 +    pthread_t tid;
 +    pthread_create(&​tid,​ NULL, (void *(*)(void *))threadFunc,​ (void *)i);
 +    pthread_detach(tid);​
 +  }
 +  threadFunc(0);​
 +}
 +</​code>​
 +</​small>​
 +<​html></​div></​html>​
 +
 +===== Thread-specific data, thread local storage [extra] =====
 +
 +In C, data items can have different storage duration and different linkage.
 +Until C11, there existed only static, automatic or allocated storage durations.
 +Roughly:
 +\\
 + • static = global variables and static variables defined inside functions,
 +\\
 + • automatic = items on stack, automatically allocated inside functions ('​local variables'​),​
 +\\
 + • allocated = items on heap, explicitly allocated and freed.
 +\\
 +None of these cover a case when one wants a static ("​global"​) variable with
 +a separate value for each thread.
 +Such storage duration is called [[https://​en.wikipedia.org/​wiki/​Thread-local_storage|thread local]]
 +and a coupe of ways to create thread-local items are presented in this section.
 +
 +==== POSIX Thread-specific data ====
 +
 +One can create //keys// (that may be understood as identifiers of variables) and
 +then associate, for each thread separately, a value for a key.
 +To this end the following functions can be used:
 +\\
 +<​html><​span style="​float:​right"><​small>​Need header:<​br><​code>​pthread.h</​code></​small></​span>​
 +<a href="​https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_key_create.html"></​html>​
 +''​int **pthread_key_create**(pthread_key_t *//key//, void (*//​destructor//​)(void*))''​
 +<​html></​a></​html>​
 +\\
 +<​html><​a href="​https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​pthread_setspecific.html"></​html>​
 +''​int **pthread_setspecific**(pthread_key_t //key//, const void *//​value//​)''​
 +\\
 +''​void %%*%%**pthread_getspecific**(pthread_key_t //​key//​)''​
 +<​html></​a></​html>​
 +\\
 +Each key is initially associated with a ''​NULL''​ for each thread.
 +
 +<​html><​div style="​line-height:​1.2em"></​html>​
 +++++An example code that creates a key, stores and retrieves the value from two threads.|
 +<code c>
 +#include <​pthread.h>​
 +#include <​stdio.h>​
 +#include <​stdlib.h>​
 +#include <​string.h>​
 +
 +pthread_key_t mySpecificVal;​
 +
 +void printMine() {
 +  printf("​I have: %s\n", (char *)pthread_getspecific(mySpecificVal));​
 +}
 +
 +void *routine(void *arg) {
 +  char *text = malloc(4);
 +  strcpy(text,​ "​baz"​);​
 +  pthread_setspecific(mySpecificVal,​ text);
 +  printMine();​
 +  return NULL;
 +}
 +
 +int main() {
 +  char *text = malloc(4);
 +  strcpy(text,​ "​foo"​);​
 +
 +  pthread_key_create(&​mySpecificVal,​ free);
 +  pthread_setspecific(mySpecificVal,​ text);
 +
 +  pthread_t ti;
 +  pthread_create(&​ti,​ NULL, routine, NULL);
 +  pthread_join(ti,​ NULL);
 +
 +  printMine();​
 +  return 0;
 +}
 +</​code>​
 +++++
 +<​html></​div></​html>​
 +
 +==== C thread-local storage ====
 +
 +Starting with C11, a new storage duration ''​_Thread_local''​ or
 +''​[[https://​en.cppreference.com/​w/​c/​thread/​thread_local|thread_local]]''​((Until
 +C23 ''​thread_local''​ was a macro defined in ''​threads.h'',​ since C23 it
 +is a keyword with same meaning as the ''​_Thread_local''​ keyword.)) is defined.
 +Variables defined with this storage duration have unique value for each thread.
 +Notice that the ''​thread_local''​ variables can have initial values (unlike in
 +POSIX), but there is no way to provide a custom destructor (unlike in POSIX). ​
 +
  
 ===== POSIX condition variables ===== ===== POSIX condition variables =====
Line 746: Line 958:
 \\       • The thread collecting items wastes CPU on busy waiting. Point the line that wastes CPU. \\       • The thread collecting items wastes CPU on busy waiting. Point the line that wastes CPU.
 \\       • Add condition variable(s) to amend the program. \\       • Add condition variable(s) to amend the program.
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 <code c linkedlist.c>​ <code c linkedlist.c>​
 #include <​pthread.h>​ #include <​pthread.h>​
Line 831: Line 1043:
 \\ \\
 The API for these synchronisation constructs looks like a simplified POSIX API: The API for these synchronisation constructs looks like a simplified POSIX API:
-<​html><​div style="​line-height:​1em"></​html>​+<​html><​div style="​line-height:​1.2em"></​html>​
 <code c> <code c>
 #include <​stdio.h>​ #include <​stdio.h>​
os_cp/threads.txt · Last modified: 2024/04/18 11:42 by jkonczak