如何将 worker NSThread 包装在 C++ 类中

问题描述 投票:0回答:1

我需要创建一个

NSThread
对象,并传递一个 C++
class
对象。问题是从线程操作的类中“退出”这个线程。

我试过:

int threadProc(void* mpClass);
struct my_autopool { /// sterge valoarea obiectelor NS definite in functie/constructor
    my_autopool() { pool = [[NSAutoreleasePool alloc] init]; }
    ~my_autopool() { [pool release]; }
    NSAutoreleasePool *pool;
};

@interface NS_THD : NSObject {
    @public
    void* thobj;
}

@end

@implementation NS_THD

-(void)thread_start {
    hreadProc(thobj);
}

-(void)thread_close {
    printf("Close!\n");
    [NSThread close];
}
@end

class CLS_THD {
public:
    NSThread* mythread;
    NS_THD *trgt;
    void CreateThreadOsx(void* mpClass) {
        my_autopool ap;
        trgt=[[NS_THD alloc] init];
        trgt->thobj=(void*)mpClass;
        mythread = [[NSThread alloc] initWithTarget:trgt selector:@selector(thread_start) object:nil];
        [mythread start];
    }

    void ExitThreadOsx() {
        [mythread close];
        //[trgt thread_close];
    }
};

class CLS_CPP {
    CLS_THD th;
public:
    int var_check;
    CLS_CPP() {
        printf("Hello CLS_CPP!\n");
        var_check=77;
        th.CreateThreadOsx((void*)this);
    }

    void change() {
    var_check++;
    }

    void exit() {
        th.ExitThreadOsx();
    }
};

int threadProc(void* mpClass) {
    CLS_CPP *pClass = reinterpret_cast<CLS_CPP*>(mpClass);

    while([[NSThread currentThread] isCancelled] == NO) {
        printf("Check var %d\n", pClass->var_check);
        usleep(333000);
    }

    return 0;
}

我现在苦苦挣扎的关键问题是,似乎没有办法“关闭”

NSThread
对象。

如何解决这种情况?

objective-c++ nsthread
1个回答
1
投票

您要实现的设计有两个复杂之处:

  1. 当您处理非
    Foundation
    类时,在Foundation框架中创建任务后,没有太多方法可以将其安排到NSThread中。本质上,与
    NSThread
    沟通的唯一(稳健)方式是通过
    -[NSThread cancel]
    -[NSThread start]
    。该类的所有其他部分(runloop、输入源等)只能从线程内部使用(或通过
    NSObject
    的执行选择器系列方法,但这并不真正适用于您的场景)。另外,完成一个(ny)线程的唯一可靠方法是让它完成它应该执行的所有子例程,我不知道有任何定义明确的实现可以让你在执行过程中停止任务而不需要调整任务本身(例如通过偶尔的标志检查)。
  2. NSThread
    本身旨在执行包含在 Foundation 类中的任务。

为了解决这些问题,您显然需要

NSThread
的实例(而不是它的
NSRunLoop
或来源,因为访问和调用这些对象的方法违反了合同,并可能导致您进入竞争条件)在你的班级和使用新引入的类
-[NSThread initWithBlock:]
的构造函数。下面是我将如何实现这样一个类:

class CLS_CPP {
    void(^workerBlock)(NSTimer*);
    NSThread *workerThread;

public:
    int var_check;

#pragma mark Special members

    CLS_CPP(): workerBlock{ ^(NSTimer *timer){
        if ([[NSThread currentThread] isCancelled]) {
            [timer invalidate];
            return;
        }
        printf("Check var %d\n", this->var_check);
    } }, var_check{ 77 } {
        start();
    }

    CLS_CPP(const CLS_CPP&) = delete;
    CLS_CPP& operator=(const CLS_CPP&) = delete;

    ~CLS_CPP() {
        if (workerThread) {
            exit();
        }
    }

#pragma mark Actions

    void change() {
        var_check++;
    }

    void start() {
        if (workerThread) {
            exit();
        }

        workerThread = [[NSThread alloc] initWithBlock:^{
            [NSTimer scheduledTimerWithTimeInterval:0.333 repeats:YES block:workerBlock];
            while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]) {};
        }];
        [workerThread start];
    }

    void exit() {
        [workerThread cancel];
        // Waits until the thread finishes
        while (!workerThread.isFinished) {
            std::this_thread::sleep_for(std::chrono::milliseconds(128));
        }
        workerThread = nil;
    }
};

如您所见,我删除了大部分特殊功能,因为在您的情况下需要格外小心(并且有点超出所问的问题)。

start
成员函数创建一个新的
NSThread
对象并启动它。
exit()
取消线程,稍后从任务本身内部检查。
usleep(333000)
的效果是通过
NSTimer
源实现的,这对于Cocoa框架来说更加一致。

实际任务包含在

workerBlock
成员变量中,所以你可以相应地调整它。如果您在启用 ARC 的情况下编译代码,则无需执行任何额外操作即可释放
CLS_CPP
资源。

© www.soinside.com 2019 - 2024. All rights reserved.