【问题标题】:Switch between threads in C在 C 中的线程之间切换
【发布时间】:2020-03-25 21:35:15
【问题描述】:

我正在尝试编写这样的代码:

输入 1:

2
123
abc

输出:

1a2b3c

第一行是下面的行数和我必须使用的线程数(最多 10 个)。 随机字符的行可能有不同的大小。

输入 2:

5
abcdef
123456789
xyz
ghi
j

输出 2:

a1xgjb2yhc3zid4e5f6789

我正在尝试使用互斥锁,但到目前为止我无法解决它。 任何帮助表示赞赏。

这是我的代码:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h> 
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int n_strings;
char chars[10][100];
char output[1000];
pthread_t thread[10];
int count = 0;

static pthread_mutex_t mutex = PTHREAD_MUTEX_INICIALIZER;

void* mix_it_up(void* arg)
{
    char* a = (char*) arg;

    for (int i = 0; i < strlen(a); i++)
    {
        pthread_mutex_lock(&mutex);
        output[count++] = a[i];
        pthread_mutex_unlock(&mutex);
    }
}

int main(void)
{
scanf("%d", &n_strings);

for (int i = 0; i < n_strings; i++)
{
    scanf("%s", chars[i]);
}

for (int i = 0; i < n_strings; i++)
{
    pthread_create(&thread[i], NULL, mix_it_up, (void*) chars[i]);
}

for (int i = 0; i < n_strings; ++i)
{
    pthread_join(thread[i], NULL);
}

pthread_mutex_destroy(&mutex);
printf("%s\n", output);
return(0);
}

感谢任何帮助。谢谢

【问题讨论】:

  • 互斥锁只保证独占访问。它不保证线程将以任何特定顺序运行。
  • OT:为了便于阅读和理解:1) 请始终缩进代码。在每个左大括号“{”后缩进。在每个右大括号 '}' 之前取消缩进。建议每个缩进级别为 4 个空格。
  • 你需要使用线程吗?使用嵌套的 for 循环不会容易得多。
  • OT:关于:scanf("%s", chars[i]); 和类似语句 1) 始终检查返回值(不是参数值)以确保操作成功。注意:这些函数返回成功的 '输入格式转换次数。在当前代码中,除 1 以外的任何返回值都表示发生了错误。 2) 当使用%s 和/或%[...] 时,总是包含比输入缓冲区长度小1 的MAX CHARACTERS 修饰符,因为这些说明符总是在输入中附加一个NUL 字节。这避免了缓冲区溢出和由此产生的未定义行为

标签: c multithreading pthreads


【解决方案1】:

除了你的全局互斥体,还有一个全局条件变量:

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

还有一个额外的变量来指示轮到哪个线程:

static int running_thread = 0;

每个线程都需要知道它拥有哪个序列 ID。而不是从字符传递一个元素,只需传递索引

for (int i = 0; i < n_strings; i++)
{
    pthread_create(&thread[i], NULL, mix_it_up, (void*)i);
}

那么你的线程需要在一个循环中等待获得访问。然后它通过更新running_thread 并通知所有线程从等待循环中唤醒以查看是否轮到它们来传递控制权。

void* mix_it_up(void* arg)
{
    int threadid = (int)arg;

    char* a = chars[threadid];

    for (int i = 0; i < strlen(a); i++)
    {
        pthread_mutex_lock(&mutex);

        // wait for our turn
        while (running_thread != threadid)
        {
            pthread_cond_wait(&cond, &mutex); // releases locks and waits for notify
            // mutex lock is implicitly re-acquired when cond_wait returns
        }

        output[count++] = a[i];

        // indicate which thread runs next
        running_thread = (running_thread + 1) % n_strings;

        // notify other threads to wakeup from pthread_cond_wait
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

这种方法的一个缺点是每次pthread_cond_broadcast 调用都会不必要地唤醒N-2 个线程。这对于〜10个线程来说非常好。但是,如果您有数百个线程,那么每个线程唤醒、再次获取互斥锁、运行、检查是否(running_thread != threadid) 然后返回等待的开销要大得多。

更复杂的方法是让所有线程共享 N 个pthread_cond_t 而不是只有 1 个。他们这样,每个线程都可以被显式唤醒,而不是通知所有线程唤醒。如果您有超过 10 个线程,这将是一个不错的方法。请记住,您仍然需要在每次(虚假)唤醒时检查(running_thread != threadid)。换句话说,一个信号量。

【讨论】:

  • 感谢您的回答。必须将值从 int 更改为 long,但它现在有点工作。有点因为如果所有输入的大小不完全相同,它就处于无限循环中。你知道如何解决这个问题吗?
  • 我也可以选择使用信号量,我只是认为使用互斥器会更容易。如果你不介意告诉我如何正确使用它们
  • 啊……所以这是一个您需要修复的错误。假设只有两个线程。第一个线程正在处理长度为 1 的字符串。另一个字符串正在处理长度为 10 的字符串(例如)。 Thread0 将首先进入,然后将thread_running 更新为 1 并执行 cond_broadcast 以唤醒 Thread1。然后 Thread0 将退出。然后 Thread1 将执行其附加到输出的操作,然后将 thread_running 切换回零并调用 cond_broadcast 以唤醒...哎呀!
  • Thread0 没有做任何事情,所以 thread_running 被困在 0 并且没有人可以增加它。同时 Thread1 在 cond_wait 调用中被阻塞。
  • 我的建议是维持所有线程继续运行,直到具有最长输入流的线程发出退出条件的信号。如果一个没有工作的线程以thread_running==threadid 唤醒,它仍然负责增加thread_running 并返回等待。您可以通过“长线程”设置一个全局变量来表示“退出时间”。
猜你喜欢
  • 1970-01-01
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-23
相关资源
最近更新 更多