为什么不能在pthread中访问cocoa控件?
当我在cocoa中使用pthread,并希望在pthread functionsetBtnState中访问cocoa控件时,它不起作用。有什么问题吗 以下是源代码: AppController.h为什么不能在pthread中访问cocoa控件?,c,cocoa,pthreads,C,Cocoa,Pthreads,当我在cocoa中使用pthread,并希望在pthread functionsetBtnState中访问cocoa控件时,它不起作用。有什么问题吗 以下是源代码: AppController.h 1 // 2 // AppController.h 3 // PThreadTest 4 // 5 // Created by zhu on 10-9-5. 6 // Copyright 2010 __MyCompanyName__. All rights reserv
1 //
2 // AppController.h
3 // PThreadTest
4 //
5 // Created by zhu on 10-9-5.
6 // Copyright 2010 __MyCompanyName__. All rights reserved.
7 //
8
9 #import <Cocoa/Cocoa.h>
10
11
12 @interface AppController : NSObject {
13 IBOutlet NSButton *btnNew;
14 IBOutlet NSButton *btnEnd;
15 }
16
17 -(IBAction)newThread:(id)sender;
18 -(IBAction)endThread:(id)sender;
19
20 @end
为什么我更新AppController.m后它不工作,如下所示:
1 //
2 // AppController.m
3 // PThreadTest
4 //
5 // Created by zhu on 10-9-5.
6 // Copyright 2010 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "AppController.h"
10 #import <pthread.h>
11
12
13 @implementation AppController
14
15 struct mydata {
16 pthread_mutex_t mutex;
17 pthread_cond_t cond;
18 int stop;
19 NSButton *btnNew;
20 NSButton *btnEnd;
21 id obj;
22 };
23
24 struct mydata adata;
25 struct mydata *ptr;
26
27 void* mythread(void* arg) {
28 NSLog(@"new thread start...");
29 ptr->stop = 0;
30 pthread_mutex_lock(&ptr->mutex);
31 while (!ptr->stop) {
32 pthread_cond_wait(&ptr->cond, &ptr->mutex);
33 }
34 pthread_mutex_unlock(&ptr->mutex);
35 [ptr->obj performSelectorOnMainThread:@selector(setBtnState) withObject:@"YES" waitUntilDone:NO];
36 NSLog(@"current thread end...");
37 }
38
39 -(void)setBtnState:(id)aobj {
40 BOOL stop = NO;
41 if ([aobj isEqualToString:@"YES"]) {
42 stop = YES;
43 }
44 [btnNew setEnabled:stop];
45 [btnEnd setEnabled:!stop];
46 }
47
48 -(id)init {
49 self = [super init];
50 ptr = &adata;
51 pthread_mutex_init(&ptr->mutex, NULL);
52 pthread_cond_init(&ptr->cond, NULL);
53 ptr->stop = 0;
54 ptr->obj = self;
55 // ptr->btnNew = btnNew;
56 // ptr->btnEnd = btnEnd;
57 return self;
58 }
59
60 - (void)awakeFromNib {
61 ptr->btnNew = btnNew;
62 ptr->btnEnd = btnEnd;
63 }
64
65 -(IBAction)newThread:(id)sender {
66 [self setBtnState:@"NO"];
67 pthread_t pid;
68 pthread_create(&pid, NULL, mythread, NULL);
69 }
70
71 -(IBAction)endThread:(id)sender {
72 pthread_mutex_lock(&ptr->mutex);
73 ptr->stop = 1;
74 pthread_mutex_unlock(&ptr->mutex);
75 pthread_cond_signal(&ptr->cond);
76 }
77
78 @end
79
您要移动:
ptr->btnNew = btnNew;
ptr->btnEnd = btnEnd;
。。。如果您正在使用Outlet并从nib文件加载,则从nib唤醒。在调用awakeFromNib和在awakeFromNib之前调用init之前,无法保证将解析这些出口
我不确定是否可以使用pthreads与UI通信。我非常确定您必须使用NSThread和通知来与主线程上的控件进行对话 您要移动:
ptr->btnNew = btnNew;
ptr->btnEnd = btnEnd;
。。。如果您正在使用Outlet并从nib文件加载,则从nib唤醒。在调用awakeFromNib和在awakeFromNib之前调用init之前,无法保证将解析这些出口
我不确定是否可以使用pthreads与UI通信。我非常确定您必须使用NSThread和通知来与主线程上的控件进行对话 您应该只从主线程与UI交互,而不是从后台线程 这不仅仅是锁定与自己控件的交互的问题;您的控件可能会在背后与UI中的其他对象交互。例如,按钮可能与窗口交互。这可能导致竞争条件、死锁、无效/混合状态以及其他并发问题 当您在后台线程上执行一些处理工作时,它需要将结果(无论是中间结果还是最终结果)传递给用户,它需要将该通信推送到主线程。可可中有几种机制可以实现这一点: -performSelectorOnMainThread:withObject:waitUntilDone:方法允许您在主线程上运行另一个方法,可以选择等待,直到它完成。但是,对于waitUntilDone:argument,您几乎不应该通过YES,因为这会导致死锁 从Mac OS X 10.6和iOS 4.0开始,有+[NSOperationQueue mainQueue],它返回与主线程关联的NSOperationQueue实例:您可以在此队列上放置操作,它们将在主线程上运行 如果您在后台队列上使用多个操作,并且需要在主线程上执行一些依赖于所有这些操作的完成操作,那么这将非常有用。您只需使用NSOperation的依赖关系机制来设置它们之间的依赖关系,即使它们位于不同的队列上 您可以将NSOperation子类化,也可以通过+[NSBlockOperation blockOperationWithBock:]为操作体使用Objective-C块 同样从Mac OS X 10.6和iOS 4.0开始,还有Grand Central Dispatch,它允许您在与主线程关联的主队列上执行阻塞。与NSOperation相比,它是一个稍微不太详细的API,但代价是不直接支持依赖项和NSOperationQueue的一些其他功能,并且是用普通C而不是Objective-C构建的 使用这些机制时需要记住的一点是,如果没有适当的并发控制(如锁定)或使用专门的无锁数据结构和原语,则不能同时与来自多个线程的相同数据交互。你无法逃脱哦,我只是在阅读,所以我不需要带锁,或者哦,我只需要启用一个按钮,我真的不需要把它推到主线程 避免此问题的一个好方法是尽可能多地进行工作,将其分批放入离散单元,在后台处理这些单元,然后将结果转发给主线程 因此,线程化代码不是这样编写的: 将线程拆分为: 锁定文档 从文档中获取一些数据 解锁文档 处理数据 告诉主线程是启用还是禁用文档的Foo按钮 相反,线程化代码的编写方式如下: 从文档中获取一些数据 将线程拆分为: 处理数据 然后告诉主线程处理的结果 在主线程上: 根据处理结果确定是启用还是禁用文档的Foo按钮
区别在于后者是根据正在完成的工作单元编写的,而不是根据用户界面编写的,在应用程序的使用过程中,它将更容易理解,也更健壮,例如添加功能—它本质上是应用于线程的MVC。您应该只从主线程与UI交互,而不是从后台线程 这不仅仅是锁定与自己控件的交互的问题;您的控件可能会在背后与UI中的其他对象交互。例如,按钮可能与窗口交互。这会导致比赛状态恶化 冲突、死锁、无效/混合状态和其他并发问题 当您在后台线程上执行一些处理工作时,它需要将结果(无论是中间结果还是最终结果)传递给用户,它需要将该通信推送到主线程。可可中有几种机制可以实现这一点: -performSelectorOnMainThread:withObject:waitUntilDone:方法允许您在主线程上运行另一个方法,可以选择等待,直到它完成。但是,对于waitUntilDone:argument,您几乎不应该通过YES,因为这会导致死锁 从Mac OS X 10.6和iOS 4.0开始,有+[NSOperationQueue mainQueue],它返回与主线程关联的NSOperationQueue实例:您可以在此队列上放置操作,它们将在主线程上运行 如果您在后台队列上使用多个操作,并且需要在主线程上执行一些依赖于所有这些操作的完成操作,那么这将非常有用。您只需使用NSOperation的依赖关系机制来设置它们之间的依赖关系,即使它们位于不同的队列上 您可以将NSOperation子类化,也可以通过+[NSBlockOperation blockOperationWithBock:]为操作体使用Objective-C块 同样从Mac OS X 10.6和iOS 4.0开始,还有Grand Central Dispatch,它允许您在与主线程关联的主队列上执行阻塞。与NSOperation相比,它是一个稍微不太详细的API,但代价是不直接支持依赖项和NSOperationQueue的一些其他功能,并且是用普通C而不是Objective-C构建的 使用这些机制时需要记住的一点是,如果没有适当的并发控制(如锁定)或使用专门的无锁数据结构和原语,则不能同时与来自多个线程的相同数据交互。你无法逃脱哦,我只是在阅读,所以我不需要带锁,或者哦,我只需要启用一个按钮,我真的不需要把它推到主线程 避免此问题的一个好方法是尽可能多地进行工作,将其分批放入离散单元,在后台处理这些单元,然后将结果转发给主线程 因此,线程化代码不是这样编写的: 将线程拆分为: 锁定文档 从文档中获取一些数据 解锁文档 处理数据 告诉主线程是启用还是禁用文档的Foo按钮 相反,线程化代码的编写方式如下: 从文档中获取一些数据 将线程拆分为: 处理数据 然后告诉主线程处理的结果 在主线程上: 根据处理结果确定是启用还是禁用文档的Foo按钮
区别在于后者是根据正在完成的工作单元编写的,而不是根据用户界面编写的,并且在应用程序的生命周期中(例如添加功能)更容易理解和更健壮—它本质上是应用于线程的MVC。非常感谢您,以及您对NSThread的建议。当我把它移到awakeFromNib时,它已经开始工作了!我调试它,init在awakeFromNib之前被调用,这就是重点。虽然使用-awakeFromNib是保证btnNew和btnEnd非nil的方法,但从非主线程与它们交互是个坏消息,可能仍然会有问题。非常感谢,还有您对NSThread的建议。当我把它移到awakeFromNib时,它已经开始工作了!我调试它,init在awakeFromNib之前被调用,这就是重点。虽然使用-awakeFromNib是保证btnNew和btnEnd非nil的方法,但从非主线程与它们交互是个坏消息,可能仍然会有问题。克里斯,主持人是看门人,而不是教授。此外,我们没有能力改变一个问题的公认答案。如果您认为所选答案是错误的,请按此处的案例。还有,你能坐这边吗?谢谢克里斯。这是否意味着cocoa或其控件不是线程安全的。但是,如果我想在另一个线程(而不是主UI线程)中更改控件的状态,比如在新线程中进行大量计算后启用一个特殊按钮,我如何在objc或cocoa中实现这一点?在swing中,为了做到这一点,我们可以使用SwingUtilities.invokeLaterRunnable doRun.Thread-safe这个形容词太简单了。有关于在Cocoa中从非主线程执行哪些操作是安全的,哪些操作是不安全的文档;如果某些东西没有列为安全的,你通常应该假设它是不安全的。我将更新我的答案,以包含一些关于让主线程代表后台线程执行某些操作的信息。我将更新我的答案,以包含将信息传递给主线程以便在那里完成工作的方法,以及如何在应用程序设计中充分利用这些信息。谢谢。换衣服
在多线程应用程序中,在后台线程上使用cocoa控件完全是一种愚蠢的方法。我更新了我的代码,为什么不起作用?克里斯,版主是看门人,不是教授。此外,我们没有能力改变一个问题的公认答案。如果您认为所选答案是错误的,请按此处的案例。还有,你能坐这边吗?谢谢克里斯。这是否意味着cocoa或其控件不是线程安全的。但是,如果我想在另一个线程(而不是主UI线程)中更改控件的状态,比如在新线程中进行大量计算后启用一个特殊按钮,我如何在objc或cocoa中实现这一点?在swing中,为了做到这一点,我们可以使用SwingUtilities.invokeLaterRunnable doRun.Thread-safe这个形容词太简单了。有关于在Cocoa中从非主线程执行哪些操作是安全的,哪些操作是不安全的文档;如果某些东西没有列为安全的,你通常应该假设它是不安全的。我将更新我的答案,以包含一些关于让主线程代表后台线程执行某些操作的信息。我将更新我的答案,以包含将信息传递给主线程以便在那里完成工作的方法,以及如何在应用程序设计中充分利用这些信息。谢谢。在多线程应用程序中,更改后台线程上cocoa控件的状态完全是一种愚蠢的方法。我更新了我的代码,为什么不起作用?
ptr->btnNew = btnNew;
ptr->btnEnd = btnEnd;