Objective-C Cocoa如何在GCD中正确使用run循环

时间:2022-09-07 08:26:44

I'm not sure how to correctly use GCD in a run loop situation where the thread might need to be stopped. The problem starts from the outset, and how or where to use CGEventCallback (which is absent from my code). The stop button won't stop the loop, and I don't think my dispatch queue is setup properly -- along with the while loop creating a huge lag.

我不确定如何在可能需要停止线程的运行循环情况下正确使用GCD。问题从一开始就开始,以及如何或在何处使用CGEventCallback(我的代码中没有)。停止按钮不会停止循环,我不认为我的调度队列设置正确 - 与while循环一起创建一个巨大的滞后。

I've read top question-answers from the search, like this and this, but one is for iOS and the other isn't relevant. Could someone show me how to properly do this?

我已经从搜索中读到了最重要的问答,比如这个和这个,但是一个用于iOS,另一个用于不相关。有人可以告诉我如何正确地做到这一点?

my code:

// .h

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {

    IBOutlet NSTextField *textFieldBox;
    IBOutlet NSButton *stop;
}

@property (assign) IBOutlet NSWindow *window;

- (void)stop;

@end

// .m

#import "AppDelegate.h"

@implementation AppDelegate

BOOL isActive = FALSE;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [self mainMethod];
}

- (void)mainMethod {

    NSLog(@"loop started");
    isActive = TRUE;
    [self theArbitraryNonCompliantLoop];
    NSLog(@"doing other stuff");
}

- (void)stop {

    isActive = FALSE;
    return;
}

- (void)theArbitraryNonCompliantLoop {

    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(backgroundQueue, ^{

        while (isActive) {
        for (NSUInteger i = 0; i < 1000000; i++) {
            [textFieldBox setStringValue:[NSString stringWithFormat:@"%lu",(unsigned long)i]];
            }
        }
    });
}

@end

4 个解决方案

#1


2  

Ignoring the name, the for loop needs to test isActive as well. That will solve the latency issue.

忽略名称,for循环也需要测试isActive。这将解决延迟问题。

The UI update needs to be done on the main thread which is easy because you can just schedule a block on the main queue to do it.

UI更新需要在主线程上完成,这很容易,因为您可以在主队列上安排块来执行此操作。

- (void)theArbitraryNonCompliantLoop {

    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(backgroundQueue, ^{

        while (isActive) 
        {
            for (NSUInteger i = 0; isActive && i < 1000000; i++) 
            {
                dispatch_async(dispatch_get_main_queue(),
                               ^{ [textFieldBox setStringValue:[NSString stringWithFormat:@"%lu",(unsigned long)i]] };
            }
        }
    });
}

There are still some issues here. I think, as it stands it will flood the main thread's run loop with events, so some throttling will be required. You might also consider some synchronisation for the inActive instance variable in case the compiler optimises it by pulling it into a register at the beginning of the method. Also, it will be subject to race conditions thanks to caching etc.

这里还有一些问题。我认为,因为它会使主线程的运行循环充满事件,所以需要一些限制。您还可以考虑对inActive实例变量进行一些同步,以防编译器通过在方法开头将其拉入寄存器来对其进行优化。此外,由于缓存等原因,它将受到竞争条件的影响。

#2


3  

Big mistake: You are changing a UI element on a background thread. That will cause all kinds of problems. Don't do that.

大错:您正在更改后台线程上的UI元素。那会引起各种各样的问题。不要那样做。

You seem to be quite confused what a runloop is. You are also trying to confuse people by calling something "theRunLoop" that just does stuff on a background thread. Your code has nothing to do with the runloop, and until you understand what a runloop is, better keep away from it.

你似乎很困惑runloop是什么。您还试图通过调用“theRunLoop”来混淆人们,这些东西只是在后台线程上执行操作。你的代码与runloop无关,直到你理解了runloop是什么,最好远离它。

#3


1  

Why would you call an arbitrary method theRunLoop?

你为什么要调用任意方法theRunLoop?

Either way, quoting Run Loops (Threading Programming Guide):

无论哪种方式,引用Run Loops(线程编程指南):

Both Cocoa and Core Foundation provide run loop objects to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.

Cocoa和Core Foundation都提供了运行循环对象来帮助您配置和管理线程的运行循环。您的应用程序不需要显式创建这些对象;每个线程(包括应用程序的主线程)都有一个关联的运行循环对象。但是,只有辅助线程需要显式运行其运行循环。作为应用程序启动过程的一部分,应用程序框架会自动在主线程上设置并运行运行循环。

#4


0  

My guess would be that your while loop is still on its first run. The 1000000 for loop is probably taking too long which is why it still seems like the loop is still running. To test it out put an NSLog after your for loop to see if it has exited it after you changed isActive to false.

我的猜测是你的while循环仍在第一次运行。 1000000 for循环可能需要太长时间,这就是为什么循环仍然在运行的原因。为了测试它,在你的for循环之后放置一个NSLog,看看它是否在你将isActive改为false后退出了它。

#1


2  

Ignoring the name, the for loop needs to test isActive as well. That will solve the latency issue.

忽略名称,for循环也需要测试isActive。这将解决延迟问题。

The UI update needs to be done on the main thread which is easy because you can just schedule a block on the main queue to do it.

UI更新需要在主线程上完成,这很容易,因为您可以在主队列上安排块来执行此操作。

- (void)theArbitraryNonCompliantLoop {

    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(backgroundQueue, ^{

        while (isActive) 
        {
            for (NSUInteger i = 0; isActive && i < 1000000; i++) 
            {
                dispatch_async(dispatch_get_main_queue(),
                               ^{ [textFieldBox setStringValue:[NSString stringWithFormat:@"%lu",(unsigned long)i]] };
            }
        }
    });
}

There are still some issues here. I think, as it stands it will flood the main thread's run loop with events, so some throttling will be required. You might also consider some synchronisation for the inActive instance variable in case the compiler optimises it by pulling it into a register at the beginning of the method. Also, it will be subject to race conditions thanks to caching etc.

这里还有一些问题。我认为,因为它会使主线程的运行循环充满事件,所以需要一些限制。您还可以考虑对inActive实例变量进行一些同步,以防编译器通过在方法开头将其拉入寄存器来对其进行优化。此外,由于缓存等原因,它将受到竞争条件的影响。

#2


3  

Big mistake: You are changing a UI element on a background thread. That will cause all kinds of problems. Don't do that.

大错:您正在更改后台线程上的UI元素。那会引起各种各样的问题。不要那样做。

You seem to be quite confused what a runloop is. You are also trying to confuse people by calling something "theRunLoop" that just does stuff on a background thread. Your code has nothing to do with the runloop, and until you understand what a runloop is, better keep away from it.

你似乎很困惑runloop是什么。您还试图通过调用“theRunLoop”来混淆人们,这些东西只是在后台线程上执行操作。你的代码与runloop无关,直到你理解了runloop是什么,最好远离它。

#3


1  

Why would you call an arbitrary method theRunLoop?

你为什么要调用任意方法theRunLoop?

Either way, quoting Run Loops (Threading Programming Guide):

无论哪种方式,引用Run Loops(线程编程指南):

Both Cocoa and Core Foundation provide run loop objects to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.

Cocoa和Core Foundation都提供了运行循环对象来帮助您配置和管理线程的运行循环。您的应用程序不需要显式创建这些对象;每个线程(包括应用程序的主线程)都有一个关联的运行循环对象。但是,只有辅助线程需要显式运行其运行循环。作为应用程序启动过程的一部分,应用程序框架会自动在主线程上设置并运行运行循环。

#4


0  

My guess would be that your while loop is still on its first run. The 1000000 for loop is probably taking too long which is why it still seems like the loop is still running. To test it out put an NSLog after your for loop to see if it has exited it after you changed isActive to false.

我的猜测是你的while循环仍在第一次运行。 1000000 for循环可能需要太长时间,这就是为什么循环仍然在运行的原因。为了测试它,在你的for循环之后放置一个NSLog,看看它是否在你将isActive改为false后退出了它。