Thread Pools with function pointers in C

Поделиться
HTML-код
  • Опубликовано: 17 янв 2025

Комментарии • 51

  • @SaveTheGhouls
    @SaveTheGhouls 3 года назад +17

    It is a shame that you do not have more views. This video and the previous one on thread pools are probably the two most simplistic and best implementations of thread pools in C using pthread anywhere on the internet. Thank you so much for sharing this.

    • @asinegaasinega
      @asinegaasinega 3 года назад

      believe that. I am a professional with lots of hardware experience. You are spot on. Dont worry about the no views it will come but you're right. This guy is spot on.

  • @cortexa100
    @cortexa100 3 года назад +6

    learnt more in these 2 videos than 1 year of learning C, amazing

  • @akhileshkukreti6303
    @akhileshkukreti6303 3 года назад +5

    Great people do tougher things in simple way & you are one of them, great job done :)

  • @bakyt_yrysov
    @bakyt_yrysov Год назад

    I just finished watching playlists on processes and threads. Thank you so much for the content!

  • @mustafabuyuk6425
    @mustafabuyuk6425 Год назад +4

    Hi thanks for greate explanation, I just solved the homework like tihs:
    #include
    #include
    #include
    #include
    #include
    #include
    #define THREAD_NUM 4
    #define TASK_COUNT 100
    typedef struct Task {
    void (*taskFunction)(int, int, int*);
    int arg1, arg2;
    int* result;
    } Task;
    Task taskQueue[256];
    int taskCount = 0;
    pthread_mutex_t mutexQueue;
    pthread_cond_t condQueue;
    void sum(int a, int b, int* result) {
    usleep(50000);
    *result = a + b;
    }
    void product(int a, int b, int* result) {
    usleep(50000);
    *result = a * b;
    }
    void executeTask(Task* task) {
    task->taskFunction(task->arg1, task->arg2, task->result);
    printf("Result of task (%d, %d) is %d
    ", task->arg1, task->arg2, *(task->result));
    }
    void submitTask(Task task) {
    pthread_mutex_lock(&mutexQueue);
    taskQueue[taskCount] = task;
    taskCount++;
    pthread_mutex_unlock(&mutexQueue);
    pthread_cond_signal(&condQueue);
    }
    void* startThread(void* args) {
    while (1) {
    Task task;
    pthread_mutex_lock(&mutexQueue);
    while (taskCount == 0) {
    pthread_cond_wait(&condQueue, &mutexQueue);
    }
    task = taskQueue[0];
    int i;
    for (i = 0; i < taskCount - 1; i++) {
    taskQueue[i] = taskQueue[i + 1];
    }
    taskCount--;
    pthread_mutex_unlock(&mutexQueue);
    executeTask(&task);
    }
    }
    int main(int argc, char* argv[]) {
    pthread_t th[THREAD_NUM];
    pthread_mutex_init(&mutexQueue, NULL);
    pthread_cond_init(&condQueue, NULL);
    int i;
    int results[TASK_COUNT];
    for (i = 0; i < THREAD_NUM; i++) {
    if (pthread_create(&th[i], NULL, &startThread, NULL) != 0) {
    perror("Failed to create the thread");
    }
    }
    srand(time(NULL));
    for (i = 0; i < TASK_COUNT; i++) {
    Task t = {
    .taskFunction = i % 2 == 0 ? &sum : &product,
    .arg1 = rand() % 100,
    .arg2 = rand() % 100,
    .result = &results[i]
    };
    submitTask(t);
    }
    for (i = 0; i < THREAD_NUM; i++) {
    if (pthread_join(th[i], NULL) != 0) {
    perror("Failed to join the thread");
    }
    }
    for (i = 0; i < TASK_COUNT; i++) {
    printf("Result %d: %d
    ", i, results[i]);
    }
    pthread_mutex_destroy(&mutexQueue);
    pthread_cond_destroy(&condQueue);
    return 0;
    }
    is it correct for return value for task

    • @CodeVault
      @CodeVault  Год назад

      Yes, that's also a solution. Good job!
      You could also just store the result directly in the Task struct and then you wouldn't even need another array

  • @weydans
    @weydans 4 года назад +6

    Hello!
    I'm from Brazil,
    here we have severals good C programers, but, them generaly do not share knolegment,
    so that's why I'm watching your videos,
    congratulations for the good classes and don't stop, you are helping loads of people from all over the world.
    Thankyou very mutch!

  • @shefiroth12
    @shefiroth12 Год назад

    I finished the "Unix Threads in C" playlist, it was good, thanks

  • @gouthamreddy444
    @gouthamreddy444 3 года назад

    I like this kind of explanation which I compare to peeling bananas and slowly feeding it to babies. Thank you for sharing knowledge. As an experienced C programmer, watching all the thread videos in these channel gave me good refreshment of concepts in detail.

  • @RahulKumar-pk7ht
    @RahulKumar-pk7ht Год назад

    Thank you so much sir

  • @nishadkanago4393
    @nishadkanago4393 Год назад

    Thank you for this playlist

  • @algoeagle7096
    @algoeagle7096 3 года назад

    Super nice and clean. Thank you !

  • @guillaume6373
    @guillaume6373 2 года назад

    Amazing video series and channel. Thanks so much for this!

  • @navguest1740
    @navguest1740 4 года назад +1

    Thank you :)

  • @sivaram38m
    @sivaram38m Год назад

    Thanks sir! It is helpful for us...

  • @taizanyazakilama3706
    @taizanyazakilama3706 2 года назад +2

    Hi! Your video series explain most concepts about multithreading, but you've missed one concept which is not even found on any websites. It is the use of multithreading dynamically. A program that creates multiple number of threads depending on the input of the user. Could you please make a video of that as well. Suppose a program needs to be created that prints numbers between 0 to 1000 but the threads are dynamic depending on the user input. So, it needs to be split into n numbers as users want it. How is it done? Thank you in advance.

    • @CodeVault
      @CodeVault  2 года назад

      Sure I will look into this

  • @spacedoctor5620
    @spacedoctor5620 Год назад

    Great vid! I've managed to mostly port this over to Windows using its API, but I have a question not touched on here. What's the best way to keep track of when your threads are all complete? Say we have a huge physics simulation we're running, so we want to throw the work at a bunch of threads, but we don't want to continue on the main thread until all the work is complete. Would we simply use the same mutex and add a counter that we increment and decrement on job start/completion? then we simply sleep the main thread until this counter is 0?

    • @CodeVault
      @CodeVault  Год назад

      Yes, or you could use a timeout when the job count goes to 0 and, once that happens, simply stop the process. You might need a separate thread for that

  • @narasarajv5278
    @narasarajv5278 4 года назад

    Thank you

  • @lavrud112
    @lavrud112 3 года назад

    First of all, thank you very much for your videos, they are excellent!
    Would be really cool if you made a video talking about the problem of readers/writers, introducing the use of pthread_rwlock_t

  • @mitchdaniel5605
    @mitchdaniel5605 Год назад

    Say you have 2 or more threads running the same function at the same time. Is this a concern for race conditions? I’m trying to multithread a function that does some computation from global inputs, and sets it equal to a global output. The inputs and outputs are global arrays, but I’m sending it different index numbers. Would race conditions occur in this set up?
    Thanks for the videos, they’re great.

    • @CodeVault
      @CodeVault  Год назад +1

      No, this won't cause a race condition since the actual memory that is modifying is different

  • @education1019
    @education1019 2 года назад

    Would it be best practice to call free on the task object after the execute task function or will that cause other problems? thanks for the vid!

    • @CodeVault
      @CodeVault  2 года назад

      No. Since you don't know if that task object has been allocated dynamically or not. It's good practice to let the caller manage its own memory

  • @georgechacko4793
    @georgechacko4793 3 года назад

    Thank you for the videos.. they are excellent. While designing how do we normally fix the number of threads in a thread pool...

    • @CodeVault
      @CodeVault  3 года назад

      I usually just detect how many cores the computer I'm running from has and set it at that.

  • @ΜιχαήλΠουλάκης
    @ΜιχαήλΠουλάκης 3 месяца назад

    sir I would like to ask you what if lets say we created a number of threads and we would want to execute each function at certain time periods so we would have them waiting for certain signals would we still get benefit from using threadpools and the producer consumer logic??

    • @CodeVault
      @CodeVault  3 месяца назад

      The thread pools are specifically made to abstract when and how the jobs get executed... So, in this situation it would be wise to not use thread pools. Although, there might be some situations where this is useful

  • @xinwu5427
    @xinwu5427 2 месяца назад

    There is still an infinite loop in `startThread`, which never returns. Any solutions for this?

    • @CodeVault
      @CodeVault  2 месяца назад +1

      You can listen to a signal or, on a different thread, have a scanf that, when entering an option, closes the whole process

    • @xinwu5427
      @xinwu5427 2 месяца назад

      @@CodeVault thanks! Below `submitTask_done` is used to signal that all tasks have been submitted. Then, if `taskCount` equals 0 in the infinite loop in `startThread`, it will return NULL.
      ```
      --- Thread_Pools_with_function_pointers_in_C.c 2024-11-02 15:33:30.508724954 +0100
      +++ newer_version.c 2024-11-02 15:37:35.525364984 +0100
      @@ -14,6 +14,7 @@
      Task taskQueue[256];
      int taskCount = 0;
      +int submitTask_done = 0;
      pthread_mutex_t mutexQueue;
      pthread_cond_t condQueue;
      @@ -51,6 +52,10 @@
      pthread_mutex_lock(&mutexQueue);
      while (taskCount == 0) {
      + if (submitTask_done) {
      + pthread_mutex_unlock(&mutexQueue);
      + return NULL;
      + }
      pthread_cond_wait(&condQueue, &mutexQueue);
      }
      @@ -85,6 +90,7 @@
      };
      submitTask(t);
      }
      + submitTask_done = 1;
      for (i = 0; i < THREAD_NUM; i++) {
      if (pthread_join(th[i], NULL) != 0) {
      ```

  • @tanishqpatil1395
    @tanishqpatil1395 2 года назад

    I tried to trace how the threads work by adding print statements and I noticed that in the end, the loop that calls pthread_join() on each thread in th[] does not completely execute. After entering the loop and reading the first join the program is stuck. What is causing this?

    • @CodeVault
      @CodeVault  2 года назад

      That's intended. pthread_join basically returns only after the thread you joined finished its execution (so effectively puts the main thread to sleep until then). This is so that you can wait for your threads to finish execution before stopping the whole process. Otherwise, you would end up calling return; on the main function in the main thread, stopping the process, which would also stop all the threads regardless if they are running or not

    • @alimousa9086
      @alimousa9086 2 года назад

      {
      while (1) {
      int time = 0;
      Task task;
      int found = 0;
      // pthread_mutex_lock (&mutexQueue);
      // while (taskCount == 0) {
      // pthread_cond_wait (&condQueue, &mutexQueue);
      // }
      if (taskCount > 0) {
      found = 1;
      task = taskQueue[0];
      for (int i = 0; i < taskCount - 1; i++) {
      taskQueue[i] = taskQueue[i + 1];
      }
      taskCount--;
      }
      // pthread_mutex_unlock (&mutexQueue);
      if (found == 1) {
      executeTask (&task);
      }
      if (time == 0) {
      return NULL;
      }
      }
      }
      I did not use pthread_mutex_lock and it worked and with return NULL the thread join worked ;)

  • @yason1325
    @yason1325 Год назад

    Hi! I have been running tests using thread pools in order to provide accurate comparable timings when it comes to using high performance computing on an algorithm I've been playing around with (my masters thesis!) This code never reaches the return at the end of the main() function. The Pthread_Join() function appears to be the problem as the threads in the threadpool never return or call pthread_exit() in order to leave the while(1) loop, so it simply hangs on the join function. I understand that there needs to be a way to keep those threads alive long enough that they can finish their work without the main thread destroying them, but this solution doesn't allow the program to end, and the tests that I need to run need to allow for multiple different sized thread pools, so I need to be able to destroy and recreate pools multiple times in one run of the executable. I've looked into my own solutions to this problem, but to no avail. Is there a graceful way to get the threads off the cond_wait and join without having to call pthread_cancel()?

    • @CodeVault
      @CodeVault  Год назад

      A simple solution would be to add a special task to the queue that makes the threads exit. You can send it from the main thread when you finished processing what you wanted. Another solution would be to remove the while(1) in the loops and just have them execute a predefined number of tasks

  • @andazo
    @andazo 3 года назад

    First, great videos man, you explain so well and carefully this topic. Thank You so much.
    I got a question, I know that in the video you use an example of random numbers that add to each other, but in a program were I have to print the output (result) in an ordered way, how can I do that without serializing the program?. It doesn't matter the order of the execution of the threads, I only want the output to be in an ordered way. Thank you!

    • @CodeVault
      @CodeVault  3 года назад

      Uhmm, I suppose if you want just your output to be ordered in some way you could simply sort it (in an array) based on some index after everything is done (or on every insertion). Either that or you give each thread a unique index where they should store the results

  • @alimousa9086
    @alimousa9086 2 года назад

    Hello!
    This playlist is really great thank you!
    I have a question how can I actually avoid using global variables, like int taskCount and Task taskQueue?

    • @CodeVault
      @CodeVault  2 года назад +1

      You could just pass an address to a variable (like taskQueue) when initializing each thread which can be stored in the main's function memory

  • @chickentowel-ek
    @chickentowel-ek 4 года назад

    Thank you for the video. Isn't there any better way to update taskQueue array not by shifting the tasks every time? It takes O(N) time for every visit.

    •  4 года назад

      Take a look at a circular buffer, it's O(1)

    • @chickentowel-ek
      @chickentowel-ek 4 года назад

      @ It's not a circular queue, it always takes 0 index value after shifting.
      for (i = 0; i < taskCount - 1; i++) {
      taskQueue[i] = taskQueue[i + 1];
      }
      It may not be an issue if taskCount is small otherwise it costs some time.

    • @CodeVault
      @CodeVault  4 года назад +1

      If you implement the queue on a linked list and not on an array, the complexity when updating should be O(1). Maybe you also need to store both head and tail of the list.

    • @chickentowel-ek
      @chickentowel-ek 4 года назад

      @@CodeVault Thank you , linked list can be the right solution as you said. I was thinking circular queue with an array but it may be also concerned for overflow situation.

  • @ESPPsycho
    @ESPPsycho Год назад

    Ball3r

  • @k_ushagra
    @k_ushagra 4 года назад +1

    Love your videos !
    first comment !! :-D