c - 为什么我的函数没有运行并出现无效内存错误?

标签 c multithreading ucontext

我正在尝试模拟线程 swapcontext() 等,但我有一些问题:

  1. 回调函数未运行
  2. 我从 free() 中收到内存错误: free():无效大小 已中止
  3. 如何等待所有线程完成?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <stdarg.h>

#define STACK_SIZE 1024

typedef struct thread {
    ucontext_t context;
    void* stack;
    int done;
} thread_t;

thread_t thread1, thread2;

void thread1_function(int argc, ...) {
    va_list args;
    va_start(args, argc);

    for (int i = 0; i < 10; i++) {
        printf("Thread 1 count: %d\n", i);
        swapcontext(&thread1.context, &thread2.context);
    }

    thread1.done = 1;
    printf("Thread 1 done\n");

    va_end(args);
}

void thread2_function() {
    for (int i = 0; i < 10; i++) {
        printf("Thread 2 count: %d\n", i);
        swapcontext(&thread2.context, &thread1.context);
    }

    printf("Thread 2 done\n");
    thread2.done = 1;
}

int main() {
    // Allocate stacks for each thread
    thread1.stack = malloc(STACK_SIZE);
    thread2.stack = malloc(STACK_SIZE);

    // Initialize the execution context for each thread
    getcontext(&thread1.context);
    thread1.context.uc_stack.ss_sp = thread1.stack;
    thread1.context.uc_stack.ss_size = STACK_SIZE;
    makecontext(&thread1.context, (void (*)())thread1_function, 1, 2, 0);

    getcontext(&thread2.context);
    thread2.context.uc_stack.ss_sp = thread2.stack;
    thread2.context.uc_stack.ss_size = STACK_SIZE;
    makecontext(&thread2.context, (void (*)())thread2_function, 0);

    // Switch to thread1
    swapcontext(&thread1.context, &thread2.context);

    // Free stacks
    free(thread1.stack);
    free(thread2.stack);

    return 0;
}

当我运行代码时,应该运行我的回调函数,而不是出现以下错误并且根本没有该函数(据我所知):

线程 2 计数:0 free():无效大小 已中止

最佳答案

在我的测试中,STACK_SIZE 必须设置为至少 6 KB,否则我会收到错误消息 free(): invalid size 或段错误。因此,我建议将 STACK_SIZE 设置为至少 32 KB,以便合理确定堆栈大小足够。

在您发布的代码中,您设置了

  • thread1.context 运行函数thread1_function
  • thread2.context 运行函数 thread2_function

但是,在行

swapcontext(&thread1.context, &thread2.context);

在函数 main 中,您使用当前上下文覆盖 thread1.context 并执行 thread2.context。这意味着 thread1.context 不再设置为执行函数 thread1_function,而是设置为执行 main 的其余部分。

因为您现在正在执行thread2.context,所以函数thread2_function将被执行。该函数将首先打印

Thread 2 count: 0

然后它将执行这一行:

swapcontext(&thread2.context, &thread1.context);

如前所述,thread1.context 不再设置为执行函数 thread1_function,而是设置为执行 main< 的其余部分。因此,在调用 swapcontext 后,程序将执行 main 中的以下几行:

// Free stacks
free(thread1.stack);
free(thread2.stack);

return 0;

这些行将终止程序,导致不打印任何其他内容。

为了解决此问题,您不应将 main 的上下文存储在 thread1.context 中。它应该存储在一个单独的对象中。

此外,如果您没有将 thread1.context.uclinkthread2.context.uclink 的值设置为非零值,那么程序将thread1_functionthread2_function 返回后立即终止。

一种可能的解决方案是将成员uclink 设置为正在执行函数main 的上下文。如果两个线程都完成,则可以将函数 main 编程为终止,否则它将把上下文设置为尚未完成的线程。

另一个问题是该行

swapcontext(&thread1.context, &thread2.context);

如果线程 2 已经完成,则函数 thread1_function 中的内容没有意义,因为在这种情况下,将上下文设置为线程 2 将导致线程 2 重复其最终时间片(堆栈已经被线程2的最后一个时间片使用,可能导致程序崩溃)。

因此,该行应更改为

if ( !thread2.done ) {
    swapcontext(&thread1.context, &thread2.context);
}

函数thread2_function也有同样的问题。

这是一个修复上述所有错误的解决方案:

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <stdarg.h>

#define STACK_SIZE 32768

typedef struct thread {
    ucontext_t context;
    void* stack;
    int done;
} thread_t;

thread_t thread1, thread2;
ucontext_t main_context;

void thread1_function(int argc, ...) {
    va_list args;
    va_start(args, argc);

    for (int i = 0; i < 10; i++) {
        printf("Thread 1 count: %d\n", i);
        if ( !thread2.done ) {
            swapcontext(&thread1.context, &thread2.context);
        }
    }

    thread1.done = 1;
    printf("Thread 1 done\n");

    va_end(args);
}

void thread2_function() {
    for (int i = 0; i < 10; i++) {
        printf("Thread 2 count: %d\n", i);
        if ( !thread1.done ) {
            swapcontext(&thread2.context, &thread1.context);
        }
    }

    printf("Thread 2 done\n");
    thread2.done = 1;
}

int main() {
    // Allocate stacks for each thread
    thread1.stack = malloc(STACK_SIZE);
    thread2.stack = malloc(STACK_SIZE);

    // Initialize the execution context for each thread
    getcontext(&thread1.context);
    thread1.context.uc_stack.ss_sp = thread1.stack;
    thread1.context.uc_stack.ss_size = STACK_SIZE;
    thread1.context.uc_link = &main_context;
    makecontext(&thread1.context, (void (*)())thread1_function, 1, 2, 0);

    getcontext(&thread2.context);
    thread2.context.uc_stack.ss_sp = thread2.stack;
    thread2.context.uc_stack.ss_size = STACK_SIZE;
    thread2.context.uc_link = &main_context;
    makecontext(&thread2.context, (void (*)())thread2_function, 0);

    // Switch to thread1
    swapcontext( &main_context, &thread1.context );

    // If a thread is still running, set context to that thread
    if ( !thread1.done ) {
        setcontext( &thread1.context );
    }
    if ( !thread2.done ) {
        setcontext( &thread2.context );
    }

    // Free stacks
    free(thread1.stack);
    free(thread2.stack);

    return 0;
}

该程序有以下输出:

Thread 1 count: 0
Thread 2 count: 0
Thread 1 count: 1
Thread 2 count: 1
Thread 1 count: 2
Thread 2 count: 2
Thread 1 count: 3
Thread 2 count: 3
Thread 1 count: 4
Thread 2 count: 4
Thread 1 count: 5
Thread 2 count: 5
Thread 1 count: 6
Thread 2 count: 6
Thread 1 count: 7
Thread 2 count: 7
Thread 1 count: 8
Thread 2 count: 8
Thread 1 count: 9
Thread 2 count: 9
Thread 1 done
Thread 2 done

关于c - 为什么我的函数没有运行并出现无效内存错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75513673/

相关文章:

c - 整个文件未读取

C 和 Lua - 将 native 对象实例传递给 native Lua 函数

android - 如何在android中停止线程(进度条)

.net - 线程加载(等待)屏幕

c - getcontext 系统调用 (ucontext.h) 的真正作用是什么?

c - 创建字符数组的方法之间的区别

c++ - 多线程和模板化单例竞争条件

c - 使用 ucontext 和信号的 mac OSX 上的用户线程调度 API

c++ - setcontext 和 makecontext 调用通用函数指针

c - 循环链表删除中的垃圾值