2

我在用户线程库上看到了一些问题,但似乎没有一个可以回答我的问题。我能够创建线程、运行它们、取消它们并退出它们。由于某种原因,我不能做的是让线程返回数据。

当我初始化我的线程库时,我设置我的退出线程上下文如下:

getcontext(&threadEnd);
threadEnd.uc_stack.ss_sp = (char *)malloc(SIGSTKSZ);
threadEnd.uc_stack.ss_size = SIGSTKSZ;
makecontext(&threadEnd, (void *) thread_exit, 1, &ReturnValue);

我创建一个线程并将其分配如下:

thread->myContext.uc_stack.ss_sp = (char *) malloc(SIGSTKSZ);
thread->myContext.uc_stack.ss_size = SIGSTKSZ;
thread->myContext.uc_link = &threadEnd;

当函数返回并调用 thread_exit()时:

    void thread_exit(void* retval){
    int* test;
    if (retval != NULL)
    {
        test = (int*) retval;
        printf("Returned value: %i\n", *test);
        fflush(stdout);
    }

打印输出始终为“返回值:0”

被调用的函数返回一个指向整数的指针。

我究竟做错了什么?

4

4 回答 4

1

如果您在 GBD makecontext 中单步执行您的程序,请不要保存用于创建上下文的函数的返回值。

我的实验示例:(观察 rax 寄存器):

在退货声明中:

thread1 (arg=0x1) at test_create_join.c:14
14      return (void *)11;
Value returned is $1 = 19
(gdb) info registers
rax            0x13 19
---

返回后:

(gdb) step
15  }
(gdb) info registers
rax            0xb  11

内部上下文切换:

__start_context () at ../sysdeps/unix/sysv/linux/x86_64/__start_context.S:32
32  ../sysdeps/unix/sysv/linux/x86_64/__start_context.S: No such file or directory.
(gdb) info registers
rax            0xb  11

您可以看到一些指令保留了返回值,但经过几个步骤后它变为 0。显然它特定于 x86_64 拱门,但我认为它可能与大多数拱门相同(即 makecontext 的行为)

现在,如果您需要线程函数的返回值,您可以采用另一种方式。只需创建一个线程处理程序来运行您的线程并使用该处理程序来创建新的上下文。在这里,您可以获得要作为线程运行的函数的返回值,并将其保存在线程控制块结构中以供以后使用。

typedef struct {
    thread_start_routine start;
    void *arg;
} entry_point;

static void thread_runner(void *args) {
    exit_critical_section();

    entry_point *entry = (entry_point *) args;

    // run the thread and get the exit code
    current->retcode = entry->start(entry->arg);
}
于 2015-05-28T09:53:55.280 回答
0

您需要一些简单的同步来等待一个线程。工作线程设置变量测试和信号。

// Pseudo code
// worker thread
retval = &VALUE; // retval is pointer?
SignalEvent( hEvent );

和主线程:

// Pseudo code
// main thread
int* test;

hEvent = CreateEvent();

WaitOnEvent( hEvent );

if (retval != NULL)
{
    test = (int*) retval;
    printf("Returned value: %i\n", *test);
    fflush(stdout);
}

对于许多线程,您需要决定是简单地等待它们中的任何一个,还是等待任何一个但直到它们都发出信号,或者您想等到所有发出信号然后检查值。此外,我们通常会考虑等待事件的线程是否在等待时进行一些空闲循环,并可能做一些其他工作(稍微复杂一点的情况)。

如果您使用 C++,我可以提供一个带有 C++ 11 条件变量的示例,但对于 C,它通常是 POSIX 或 Win32 API,但我们可以使用伪代码。Win32 SetEventWaitForSingleObject。这个 POSIX 主题几乎涵盖了您的条件变量

请注意,随着线程完成一个线程也会触发信号,因此在 Win32 中您可以直接等待线程句柄,但这种方法可能不可移植。此外,线程及其同步不是 C 语言标准的一部分,因此仅适用于同时提供两者的特定操作系统才有意义。

于 2015-01-16T07:28:29.430 回答
0

我们不应该在makecontext()的参数列表中传递指针。它们必须是手册中指定的整数:

函数 func 被调用,并传递了 argc 后面的一系列整数 (int) 参数;调用者必须在 argc 中指定这些参数的数量。

在某些架构上,整数与指针具有相同的大小(例如 32 位),但在其他架构上,指针是 64 位,而整数是 32 位长。在最近的 GLIBC/Linux x86_64 架构上,参数在上下文中存储为“long long”整数。因此,这可能会起作用,因为这会使参数存储为 64 位值(与指针的大小兼容),但这不是可移植的。因此,这可以解释为什么您没有通过“&ReturnValue”指针获得正确的值。

于 2021-01-18T12:49:03.117 回答
0

首先,在向 提供参数时makecontext,最后一个应该始终是NULL

makecontext(&threadEnd, (void *) thread_exit, 1, &ReturnValue, NULL);

我有同样的问题,我处理它有点不同,即而不是使用uc_linkI stored start_routine,即在结构内部func并使用包装函数:实际调用线程函数并存储返回值:argthreadthread_runner

makecontext(&thread->ctx, (void *) thread_runner, 1, thread, NULL);

其中线程亚军是:

void thread_runner(thread_t *thread){
  void **retval = thread->func(thread->arg);
  if(retval != NULL){
    thread_exit(*retval);
  } else {
    thread_exit(NULL);
  }
}
于 2015-09-10T07:41:58.563 回答