How do I start threads in plain C?

CMultithreading

C Problem Overview


I have used fork() in C to start another process. How do I start a new thread?

C Solutions


Solution 1 - C

Since you mentioned fork() I assume you're on a Unix-like system, in which case http://en.wikipedia.org/wiki/POSIX_Threads">POSIX threads (usually referred to as pthreads) are what you want to use.

Specifically, pthread_create() is the function you need to create a new thread. Its arguments are:

int  pthread_create(pthread_t  *  thread, pthread_attr_t * attr, void *
   (*start_routine)(void *), void * arg);

The first argument is the returned pointer to the thread id. The second argument is the thread arguments, which can be NULL unless you want to start the thread with a specific priority. The third argument is the function executed by the thread. The fourth argument is the single argument passed to the thread function when it is executed.

Solution 2 - C

AFAIK, ANSI C doesn't define threading, but there are various libraries available.

If you are running on Windows, link to msvcrt and use _beginthread or _beginthreadex.

If you are running on other platforms, check out the pthreads library (I'm sure there are others as well).

Solution 3 - C

C11 threads + C11 atomic_int

Added to glibc 2.28. Tested in Ubuntu 18.10 amd64 (comes with glic 2.28) and Ubuntu 18.04 (comes with glibc 2.27) by compiling glibc 2.28 from source: https://stackoverflow.com/questions/847179/multiple-glibc-libraries-on-a-single-host/52454603#52454603

Example adapted from: https://en.cppreference.com/w/c/language/atomic

main.c

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>

atomic_int atomic_counter;
int non_atomic_counter;

int mythread(void* thr_data) {
    (void)thr_data;
    for(int n = 0; n < 1000; ++n) {
        ++non_atomic_counter;
        ++atomic_counter;
        // for this example, relaxed memory order is sufficient, e.g.
        // atomic_fetch_add_explicit(&atomic_counter, 1, memory_order_relaxed);
    }
    return 0;
}

int main(void) {
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], mythread, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("atomic     %d\n", atomic_counter);
    printf("non-atomic %d\n", non_atomic_counter);
}

GitHub upstream.

Compile and run:

gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

Possible output:

atomic     10000
non-atomic 4341

The non-atomic counter is very likely to be smaller than the atomic one due to racy access across threads to the non-atomic variable.

See also: https://stackoverflow.com/questions/2353371/how-to-do-an-atomic-increment-and-fetch-in-c/30878480#30878480

Disassembly analysis

Disassemble with:

gdb -batch -ex "disassemble/rs mythread" main.out

contains:

17              ++non_atomic_counter;
   0x00000000004007e8 <+8>:     83 05 65 08 20 00 01    addl   $0x1,0x200865(%rip)        # 0x601054 <non_atomic_counter>

18              __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
   0x00000000004007ef <+15>:    f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip)        # 0x601058 <atomic_counter>

so we see that the atomic increment is done at the instruction level with the f0 lock prefix.

With aarch64-linux-gnu-gcc 8.2.0, we get instead:

11              ++non_atomic_counter;
   0x0000000000000a28 <+24>:    60 00 40 b9     ldr     w0, [x3]
   0x0000000000000a2c <+28>:    00 04 00 11     add     w0, w0, #0x1
   0x0000000000000a30 <+32>:    60 00 00 b9     str     w0, [x3]

12              ++atomic_counter;
   0x0000000000000a34 <+36>:    40 fc 5f 88     ldaxr   w0, [x2]
   0x0000000000000a38 <+40>:    00 04 00 11     add     w0, w0, #0x1
   0x0000000000000a3c <+44>:    40 fc 04 88     stlxr   w4, w0, [x2]
   0x0000000000000a40 <+48>:    a4 ff ff 35     cbnz    w4, 0xa34 <mythread+36>

so the atomic version actually has a cbnz loop that runs until the stlxr store succeed. Note that ARMv8.1 can do all of that with a single LDADD instruction.

This is analogous to what we get with C++ std::atomic: https://stackoverflow.com/questions/31978324/what-exactly-is-stdatomic/58904448#58904448

Benchmark

TODO. Crate a benchmark to show that atomic is slower.

POSIX threads

main.c

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>

enum CONSTANTS {
    NUM_THREADS = 1000,
    NUM_ITERS = 1000
};

int global = 0;
int fail = 0;
pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;

void* main_thread(void *arg) {
    int i;
    for (i = 0; i < NUM_ITERS; ++i) {
        if (!fail)
            pthread_mutex_lock(&main_thread_mutex);
        global++;
        if (!fail)
            pthread_mutex_unlock(&main_thread_mutex);
    }
    return NULL;
}

int main(int argc, char **argv) {
    pthread_t threads[NUM_THREADS];
    int i;
    fail = argc > 1;
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, main_thread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);
    assert(global == NUM_THREADS * NUM_ITERS);
    return EXIT_SUCCESS;
}

Compile and run:

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
./main.out 1

The first run works fine, the second fails due to missing synchronization.

There don't seem to be POSIX standardized atomic operations: https://stackoverflow.com/questions/1130018/unix-portable-atomic-operations

Tested on Ubuntu 18.04. GitHub upstream.

GCC __atomic_* built-ins

For those that don't have C11, you can achieve atomic increments with the __atomic_* GCC extensions.

main.c

#define _XOPEN_SOURCE 700
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>

enum Constants {
    NUM_THREADS = 1000,
};

int atomic_counter;
int non_atomic_counter;

void* mythread(void *arg) {
    (void)arg;
    for (int n = 0; n < 1000; ++n) {
        ++non_atomic_counter;
        __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
    }
    return NULL;
}

int main(void) {
    int i;
    pthread_t threads[NUM_THREADS];
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, mythread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);
    printf("atomic     %d\n", atomic_counter);
    printf("non-atomic %d\n", non_atomic_counter);
}

Compile and run:

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

Output and generated assembly: the same as the "C11 threads" example.

Tested in Ubuntu 16.04 amd64, GCC 6.4.0.

Solution 4 - C

pthreads is a good start, look here

Solution 5 - C

Threads are not part of the C standard, so the only way to use threads is to use some library (eg: POSIX threads in Unix/Linux, _beginthread/_beginthreadex if you want to use the C-runtime from that thread or just CreateThread Win32 API)

Solution 6 - C

Check out the pthread (POSIX thread) library.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHanno FietzView Question on Stackoverflow
Solution 1 - CCommodore JaegerView Answer on Stackoverflow
Solution 2 - CBrannonView Answer on Stackoverflow
Solution 3 - CCiro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 4 - CepatelView Answer on Stackoverflow
Solution 5 - CbotismariusView Answer on Stackoverflow
Solution 6 - CJay ConrodView Answer on Stackoverflow