How to stop a thread created with dispatch_async?

2019-08-09 06:25发布

I want to forcibly stop the thread created by dispatch_async if its in use for too much time, for example pass over 5 minutes. By searching over the internet, I got some one thought there was no way to stop the thread, does any one know that?

In my imagine, I want to create a NSTimer to stop the thread when time specified passed.

+ (void)stopThread:(NSTimer*)timer
{
    forcibly stop the thread???
}

+ (void)runScript:(NSString *)scriptFilePath
{
    [NSTimer scheduledTimerWithTimeInterval:5*60 target:self selector:@selector(stopThread:) userInfo:nil repeats:NO];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [LuaBridge runLuaFile:scriptFilePath];

    });
} 

My runLuaScript method:

+ (void)runLuaFile:(NSString *)filePath
{

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
    if (error2) {
        fprintf(stderr, "%s", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    lua_close(L);
}

Dear @Martin R, should I use lstop like that, and when I want to stop the thread, just call stopLuaRunning method?

static lua_State *L = NULL;

+ (void)runLuaFile:(NSString *)filePath
{

    L = luaL_newstate();
    luaL_openlibs(L);

    int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
    if (error2) {
        fprintf(stderr, "%s", lua_tostring(L, -1));
        lua_pop(L, 1);
    }

    lua_close(L);
}

+ (void)stopLuaRunning:(lua_State *L)
{
    lua_sethook(L, NULL, 0, 0);
    luaL_error(L, "interrupted!");
}

4条回答
可以哭但决不认输i
2楼-- · 2019-08-09 06:31

By searching over the internet, I got some one though there was no way to stop the thread, does any one know that?

Don't bother; It's not yours to stop. If you have a reference to a queue, then you can call dispatch_release and it will be destroyed at the appropriate time, but you would not do this with the global queue.

Killing that thread would just kill a thread in the queue's pool and should be considered as good as undefined behavior.

If you want to control a thread's lifetime, create your own thread and interact with its run loop. But do ensure your programs return from their implementations normally -- don't just kill stuff because it isn't working for you or never returning. Martin R mentioned how this would happen -- your task should support timeout, cancellation, or another means to stop itself in the event the task has gone rogue.

Wain's also mentioned a good middle ground.

查看更多
何必那么认真
3楼-- · 2019-08-09 06:42

You cannot kill a running block. You have to implement runLuaFile in a way that it works asynchronously and can be cancelled.

For example if running the script is done via NSTask, you can use terminate to kill the task if it is running too long.

NSOperation will probably not help because cancel relies on the operation to be "cooperative": the operation has to check regularly if it has been cancelled. That will not stop the running runLuaFile method.

UPDATE: From inspecting the source code "lua.c" of the Lua interpreter, it seems to me that you can cancel a running script using lua_sethook.

A very simple implementation (using a static variable for the Lua state) would be:

static lua_State *L = NULL;

+ (void)runLuaFile:(NSString *)filePath
{
    L = luaL_newstate();
    luaL_openlibs(L);
    int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
    if (error2) {
        fprintf(stderr, "%s", lua_tostring(L, -1));
        lua_pop(L, 1);
    }
    lua_close(L);
    L = NULL;
}

static void lstop (lua_State *L, lua_Debug *ar)
{
    lua_sethook(L, NULL, 0, 0);
    luaL_error(L, "interrupted!");
}

+ (void)stopLuaRunning
{
    if (L != NULL)
        lua_sethook(L, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}

A more elegant solution would use store the Lua state in an instance variable of the class and make runLuaFile and stopLuaRunning instance methods instead of class methods.

查看更多
Lonely孤独者°
4楼-- · 2019-08-09 06:42

Use NSOperation and NSOperationQueue.

Here is a long, but helpful guide.

http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

The key point for you in this case, is there example of overriding main.

@interface MyLengthyOperation: NSOperation
@end

@implementation MyLengthyOperation
- (void)main {
    // a lengthy operation
    @autoreleasepool {
        for (int i = 0 ; i < 10000 ; i++) {

        // is this operation cancelled?
        if (self.isCancelled)
            break;

        NSLog(@"%f", sqrt(i));
        }
    }
}
@end

Notice the if (self.isCancelled) in the middle of the loop.

This is the "modern" iOS way of managing background operations without creating your own threads and managing them directly.

查看更多
狗以群分
5楼-- · 2019-08-09 06:44

You should use NSOperation and NSOperationQueue as they have built in support for cancellation so your operation can check if it's cancelled and your timer just calls cancel on the operation.

查看更多
登录 后发表回答