利用iphone的多线程实现“售票系统”(手把手教你iphone开发 - 基础篇)
2012-01-19 14:03
423 查看
作者:孙东风 2009-11-10 (请尊重作者劳动成果,转载务必注明出处)
http://blog.csdn.net/dongfengsun/archive/2009/11/10/4794010.aspx
Java 因为其本身支持多线程而给程序员带来很多方便,其实在 iphone 的开发中也支持多线程编程,并且一点也不比 java 麻烦。
在这篇文章中,笔者就拿大多数 Java 教程中经典的“售票系统多线程”作为实际例子,在 iphone 中进行同样的实现。
下面是 java 版本的“售票系统多线程”代码:
package demo;
public class SellTickets implements Runnable{
private int tickets=100;
public void run() {
int count=0;
while (true)
{
// 上锁
synchronized(this){
if (tickets>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count=100-tickets;
System.out.println(" 当前票数是 :"+tickets+" 售出 "+count
+" 线程名: "+Thread.currentThread().getName());
tickets--;
}else{
break;
}
}
}
}
public static void main(String[] args) {
SellTickets r=new SellTickets();
Thread t1=new Thread(r,"t1");
t1.start();
Thread t2=new Thread(r,"t2");
t2.start();
Thread t3=new Thread(r,"t3");
t3.start();
Thread t4=new Thread(r,"t4");
t4.start();
}
}
以上 java 版本的代码执行后控制台输出如下:
当前票数是 :100 售出 0 线程名: t1
当前票数是 :99 售出 1 线程名: t2
当前票数是 :98 售出 2 线程名: t3
当前票数是 :97 售出 3 线程名: t4
当前票数是 :96 售出 4 线程名: t1
当前票数是 :95 售出 5 线程名: t2
当前票数是 :94 售出 6 线程名: t3
当前票数是 :93 售出 7 线程名: t4
当前票数是 :92 售出 8 线程名: t1
当前票数是 :91 售出 9 线程名: t2
当前票数是 :90 售出 10 线程名: t3
当前票数是 :89 售出 11 线程名: t4
当前票数是 :88 售出 12 线程名: t1
当前票数是 :87 售出 13 线程名: t2
当前票数是 :86 售出 14 线程名: t3
当前票数是 :85 售出 15 线程名: t4
当前票数是 :84 售出 16 线程名: t1
当前票数是 :83 售出 17 线程名: t2
当前票数是 :82 售出 18 线程名: t3
当前票数是 :81 售出 19 线程名: t4
当前票数是 :80 售出 20 线程名: t1
当前票数是 :79 售出 21 线程名: t2
当前票数是 :78 售出 22 线程名: t3
当前票数是 :77 售出 23 线程名: t4
当前票数是 :76 售出 24 线程名: t1
当前票数是 :75 售出 25 线程名: t2
当前票数是 :74 售出 26 线程名: t3
当前票数是 :73 售出 27 线程名: t4
……
可以在 iphone 中进行同样的实现, Iphone 的 Frameworks/Foundation.framework 框架支持多线程编程,接口定义在 :
/Xcode3.1.4/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSThread.h
到相应的目录下打开此文件,可以看到绝大多数 java 中的接口这里也都能找到相应的实现,如下:
/* NSThread.h
Copyright (c) 1994-2007, Apple Inc. All rights reserved.
*/
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
@class NSArray, NSMutableDictionary, NSDate;
@interface NSThread : NSObject {
@private
id _private;
uint8_t _bytes[44];
}
+ (NSThread *)currentThread;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
+ (BOOL)isMultiThreaded;
- (NSMutableDictionary *)threadDictionary;
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
+ (void)exit;
#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
#endif
#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (NSArray *)callStackReturnAddresses;
- (void)setName:(NSString *)n;
- (NSString *)name;
- (NSUInteger)stackSize;
- (void)setStackSize:(NSUInteger)s;
- (BOOL)isMainThread;
+ (BOOL)isMainThread; // reports whether current thread is main
+ (NSThread *)mainThread;
- (id)init; // designated initializer
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
- (BOOL)isExecuting;
- (BOOL)isFinished;
- (BOOL)isCancelled;
- (void)cancel;
- (void)start;
- (void)main; // thread body method
#endif
@end
FOUNDATION_EXPORT NSString * const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSString * const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSString * const NSThreadWillExitNotification;
@interface NSObject (NSThreadPerformAdditions)
#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
#endif
#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
#endif
@end
从接口的定义中可以知道, NSThread 和大多数 iphone 的接口对象一样,有两种方式可以初始化:
一种使用 initWithTarget :(id)target selector:(SEL)selector object:(id)argument, 但需要负责在对象的 retain
count为 0 时调用对象的 release 方法清理对象。
另一种则使用所谓的 convenient method, 这个方便接口就是 detachNewThreadSelector, 这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。
因为在笔者的 iphone 版本“售票系统多线程”程序中需要设置线程的诸多参数,所以需要采用第一种方法来生成线程对象并自己启动它们。
首先,新建一个“ Window-based Application ”项目,并命名为 SellTickets ,接下来在 SellTicketsAppDelegate.h 文件中声明以下变量:
//
// SellTicketsAppDelegate.h
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
笔者在头文件中声明了两个 NSThread 的指针,下面需要在 *.m 文件中初始化并实现它们,如下:
//
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread
currentThread] name]);
tickets--;
}else
{
break;
}
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[window release];
[super dealloc];
}
@end
笔者在实现中用 alloc 初始化了两个 NSThread 对象并分别把它们命名为“ Thread-1 ”和“ Thread-2 ”,运行程序可以看到如下输出:
[Session started at 2009-11-10 14:25:26 +0800.]
2009-11-10 14:25:28.814 SellTickets[1298:4103] 当前票数是 :100, 售出 :0, 线程名 :Thread-1
2009-11-10 14:25:28.814 SellTickets[1298:4203] 当前票数是 :100, 售出 :0, 线程名 :Thread-2
2009-11-10 14:25:29.316 SellTickets[1298:4103] 当前票数是 :98, 售出 :2, 线程名 :Thread-1
2009-11-10 14:25:29.316 SellTickets[1298:4203] 当前票数是 :98, 售出 :2, 线程名 :Thread-2
2009-11-10 14:25:29.817 SellTickets[1298:4103] 当前票数是 :96, 售出 :4, 线程名 :Thread-1
2009-11-10 14:25:29.817 SellTickets[1298:4203] 当前票数是 :96, 售出 :4, 线程名 :Thread-2
2009-11-10 14:25:30.318 SellTickets[1298:4103] 当前票数是 :94, 售出 :6, 线程名 :Thread-1
2009-11-10 14:25:30.318 SellTickets[1298:4203] 当前票数是 :94, 售出 :6, 线程名 :Thread-2
2009-11-10 14:25:30.819 SellTickets[1298:4103] 当前票数是 :92, 售出 :8, 线程名 :Thread-1
2009-11-10 14:25:30.819 SellTickets[1298:4203] 当前票数是 :92, 售出 :8, 线程名 :Thread-2
2009-11-10 14:25:31.320 SellTickets[1298:4103] 当前票数是 :90, 售出 :10, 线程名 :Thread-1
2009-11-10 14:25:31.320 SellTickets[1298:4203] 当前票数是 :90, 售出 :10, 线程名 :Thread-2
2009-11-10 14:25:31.820 SellTickets[1298:4103] 当前票数是 :88, 售出 :12, 线程名 :Thread-1
2009-11-10 14:25:31.821 SellTickets[1298:4203] 当前票数是 :87, 售出 :13, 线程名 :Thread-2
2009-11-10 14:25:32.321 SellTickets[1298:4103] 当前票数是 :86, 售出 :14, 线程名 :Thread-1
2009-11-10 14:25:32.322 SellTickets[1298:4203] 当前票数是 :86, 售出 :14, 线程名 :Thread-2
2009-11-10 14:25:32.823 SellTickets[1298:4103] 当前票数是 :84, 售出 :16, 线程名 :Thread-1
2009-11-10 14:25:32.823 SellTickets[1298:4203] 当前票数是 :83, 售出 :17, 线程名 :Thread-2
2009-11-10 14:25:33.323 SellTickets[1298:4103] 当前票数是 :82, 售出 :18, 线程名 :Thread-1
2009-11-10 14:25:33.324 SellTickets[1298:4203] 当前票数是 :81, 售出 :19, 线程名 :Thread-2
2009-11-10 14:25:33.824 SellTickets[1298:4103] 当前票数是 :80, 售出 :20, 线程名 :Thread-1
2009-11-10 14:25:33.825 SellTickets[1298:4203] 当前票数是 :79, 售出 :21, 线程名 :Thread-2
2009-11-10 14:25:34.325 SellTickets[1298:4103] 当前票数是 :78, 售出 :22, 线程名 :Thread-1
2009-11-10 14:25:34.326 SellTickets[1298:4203] 当前票数是 :77, 售出 :23, 线程名 :Thread-2
2009-11-10 14:25:34.826 SellTickets[1298:4103] 当前票数是 :76, 售出 :24, 线程名 :Thread-1
2009-11-10 14:25:34.827 SellTickets[1298:4203] 当前票数是 :75, 售出 :25, 线程名 :Thread-2
2009-11-10 14:25:35.327 SellTickets[1298:4103] 当前票数是 :74, 售出 :26, 线程名 :Thread-1
2009-11-10 14:25:35.328 SellTickets[1298:4203] 当前票数是 :73, 售出 :27, 线程名 :Thread-2
2009-11-10 14:25:35.827 SellTickets[1298:4103] 当前票数是 :72, 售出 :28, 线程名 :Thread-1
2009-11-10 14:25:35.830 SellTickets[1298:4203] 当前票数是 :71, 售出 :29, 线程名 :Thread-2
2009-11-10 14:25:36.329 SellTickets[1298:4103] 当前票数是 :70, 售出 :30, 线程名 :Thread-1
2009-11-10 14:25:36.330 SellTickets[1298:4203] 当前票数是 :69, 售出 :31, 线程名 :Thread-2
2009-11-10 14:25:36.830 SellTickets[1298:4103] 当前票数是 :68, 售出 :32, 线程名 :Thread-1
2009-11-10 14:25:36.831 SellTickets[1298:4203] 当前票数是 :67, 售出 :33, 线程名 :Thread-2
2009-11-10 14:25:37.331 SellTickets[1298:4103] 当前票数是 :66, 售出 :34, 线程名 :Thread-1
2009-11-10 14:25:37.332 SellTickets[1298:4203] 当前票数是 :65, 售出 :35, 线程名 :Thread-2
2009-11-10 14:25:37.832 SellTickets[1298:4103] 当前票数是 :64, 售出 :36, 线程名 :Thread-1
2009-11-10 14:25:37.833 SellTickets[1298:4203] 当前票数是 :63, 售出 :37, 线程名 :Thread-2
2009-11-10 14:25:38.333 SellTickets[1298:4103] 当前票数是 :62, 售出 :38, 线程名 :Thread-1
2009-11-10 14:25:38.334 SellTickets[1298:4203] 当前票数是 :61, 售出 :39, 线程名 :Thread-2
2009-11-10 14:25:38.834 SellTickets[1298:4103] 当前票数是 :60, 售出 :40, 线程名 :Thread-1
2009-11-10 14:25:38.836 SellTickets[1298:4203] 当前票数是 :59, 售出 :41, 线程名 :Thread-2
2009-11-10 14:25:39.335 SellTickets[1298:4103] 当前票数是 :58, 售出 :42, 线程名 :Thread-1
2009-11-10 14:25:39.337 SellTickets[1298:4203] 当前票数是 :58, 售出 :42, 线程名 :Thread-2
2009-11-10 14:25:39.838 SellTickets[1298:4103] 当前票数是 :56, 售出 :44, 线程名 :Thread-1
2009-11-10 14:25:39.839 SellTickets[1298:4203] 当前票数是 :55, 售出 :45, 线程名 :Thread-2
2009-11-10 14:25:40.339 SellTickets[1298:4103] 当前票数是 :54, 售出 :46, 线程名 :Thread-1
2009-11-10 14:25:40.340 SellTickets[1298:4203] 当前票数是 :53, 售出 :47, 线程名 :Thread-2
2009-11-10 14:25:40.840 SellTickets[1298:4103] 当前票数是 :52, 售出 :48, 线程名 :Thread-1
2009-11-10 14:25:40.841 SellTickets[1298:4203] 当前票数是 :51, 售出 :49, 线程名 :Thread-2
2009-11-10 14:25:41.341 SellTickets[1298:4103] 当前票数是 :50, 售出 :50, 线程名 :Thread-1
2009-11-10 14:25:41.342 SellTickets[1298:4203] 当前票数是 :49, 售出 :51, 线程名 :Thread-2
2009-11-10 14:25:41.842 SellTickets[1298:4103] 当前票数是 :48, 售出 :52, 线程名 :Thread-1
2009-11-10 14:25:41.843 SellTickets[1298:4203] 当前票数是 :47, 售出 :53, 线程名 :Thread-2
2009-11-10 14:25:42.343 SellTickets[1298:4103] 当前票数是 :46, 售出 :54, 线程名 :Thread-1
2009-11-10 14:25:42.344 SellTickets[1298:4203] 当前票数是 :45, 售出 :55, 线程名 :Thread-2
2009-11-10 14:25:42.844 SellTickets[1298:4103] 当前票数是 :44, 售出 :56, 线程名 :Thread-1
2009-11-10 14:25:42.845 SellTickets[1298:4203] 当前票数是 :43, 售出 :57, 线程名 :Thread-2
2009-11-10 14:25:43.345 SellTickets[1298:4103] 当前票数是 :42, 售出 :58, 线程名 :Thread-1
2009-11-10 14:25:43.346 SellTickets[1298:4203] 当前票数是 :42, 售出 :58, 线程名 :Thread-2
2009-11-10 14:25:43.846 SellTickets[1298:4103] 当前票数是 :40, 售出 :60, 线程名 :Thread-1
2009-11-10 14:25:43.847 SellTickets[1298:4203] 当前票数是 :39, 售出 :61, 线程名 :Thread-2
2009-11-10 14:25:44.347 SellTickets[1298:4103] 当前票数是 :38, 售出 :62, 线程名 :Thread-1
2009-11-10 14:25:44.348 SellTickets[1298:4203] 当前票数是 :37, 售出 :63, 线程名 :Thread-2
2009-11-10 14:25:44.848 SellTickets[1298:4103] 当前票数是 :36, 售出 :64, 线程名 :Thread-1
2009-11-10 14:25:44.849 SellTickets[1298:4203] 当前票数是 :35, 售出 :65, 线程名 :Thread-2
2009-11-10 14:25:45.349 SellTickets[1298:4103] 当前票数是 :34, 售出 :66, 线程名 :Thread-1
2009-11-10 14:25:45.350 SellTickets[1298:4203] 当前票数是 :33, 售出 :67, 线程名 :Thread-2
2009-11-10 14:25:45.850 SellTickets[1298:4103] 当前票数是 :32, 售出 :68, 线程名 :Thread-1
2009-11-10 14:25:45.851 SellTickets[1298:4203] 当前票数是 :31, 售出 :69, 线程名 :Thread-2
2009-11-10 14:25:46.350 SellTickets[1298:4103] 当前票数是 :30, 售出 :70, 线程名 :Thread-1
2009-11-10 14:25:46.351 SellTickets[1298:4203] 当前票数是 :29, 售出 :71, 线程名 :Thread-2
2009-11-10 14:25:46.851 SellTickets[1298:4103] 当前票数是 :28, 售出 :72, 线程名 :Thread-1
2009-11-10 14:25:46.853 SellTickets[1298:4203] 当前票数是 :27, 售出 :73, 线程名 :Thread-2
2009-11-10 14:25:47.352 SellTickets[1298:4103] 当前票数是 :26, 售出 :74, 线程名 :Thread-1
2009-11-10 14:25:47.354 SellTickets[1298:4203] 当前票数是 :25, 售出 :75, 线程名 :Thread-2
可以看到,因为两个线程共享变量 tickets 和 count ,开头的输出就产生了异常情况, iphone 虽然没有提供类似 java 下的synchronized 关键字,但提供了 NSCondition 对象接口。查看 NSCondition 的接口说明可以看出, NSCondition 是 iphone 下的锁对象,所以我们需要让代码成为线程安全的,修改头文件如下:
//
// SellTicketsAppDelegate.h
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
然后在实现中添加如下代码:
//
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
//
锁对象
ticketCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
//
上锁
[ticketsCondition lock];
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread
currentThread] name]);
tickets--;
}else
{
break;
}
[ticketsCondition unlock];
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[ticketsCondition release];
[window release];
[super dealloc];
}
@end
最后千万别忘记在 dealloc 方法中调用对象的 release 进行资源释放,现在再次运行下看看, iphone 版本的“售票系统多线程”程序是否跑起来了:)
http://blog.csdn.net/dongfengsun/archive/2009/11/10/4794010.aspx
Java 因为其本身支持多线程而给程序员带来很多方便,其实在 iphone 的开发中也支持多线程编程,并且一点也不比 java 麻烦。
在这篇文章中,笔者就拿大多数 Java 教程中经典的“售票系统多线程”作为实际例子,在 iphone 中进行同样的实现。
下面是 java 版本的“售票系统多线程”代码:
package demo;
public class SellTickets implements Runnable{
private int tickets=100;
public void run() {
int count=0;
while (true)
{
// 上锁
synchronized(this){
if (tickets>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count=100-tickets;
System.out.println(" 当前票数是 :"+tickets+" 售出 "+count
+" 线程名: "+Thread.currentThread().getName());
tickets--;
}else{
break;
}
}
}
}
public static void main(String[] args) {
SellTickets r=new SellTickets();
Thread t1=new Thread(r,"t1");
t1.start();
Thread t2=new Thread(r,"t2");
t2.start();
Thread t3=new Thread(r,"t3");
t3.start();
Thread t4=new Thread(r,"t4");
t4.start();
}
}
以上 java 版本的代码执行后控制台输出如下:
当前票数是 :100 售出 0 线程名: t1
当前票数是 :99 售出 1 线程名: t2
当前票数是 :98 售出 2 线程名: t3
当前票数是 :97 售出 3 线程名: t4
当前票数是 :96 售出 4 线程名: t1
当前票数是 :95 售出 5 线程名: t2
当前票数是 :94 售出 6 线程名: t3
当前票数是 :93 售出 7 线程名: t4
当前票数是 :92 售出 8 线程名: t1
当前票数是 :91 售出 9 线程名: t2
当前票数是 :90 售出 10 线程名: t3
当前票数是 :89 售出 11 线程名: t4
当前票数是 :88 售出 12 线程名: t1
当前票数是 :87 售出 13 线程名: t2
当前票数是 :86 售出 14 线程名: t3
当前票数是 :85 售出 15 线程名: t4
当前票数是 :84 售出 16 线程名: t1
当前票数是 :83 售出 17 线程名: t2
当前票数是 :82 售出 18 线程名: t3
当前票数是 :81 售出 19 线程名: t4
当前票数是 :80 售出 20 线程名: t1
当前票数是 :79 售出 21 线程名: t2
当前票数是 :78 售出 22 线程名: t3
当前票数是 :77 售出 23 线程名: t4
当前票数是 :76 售出 24 线程名: t1
当前票数是 :75 售出 25 线程名: t2
当前票数是 :74 售出 26 线程名: t3
当前票数是 :73 售出 27 线程名: t4
……
可以在 iphone 中进行同样的实现, Iphone 的 Frameworks/Foundation.framework 框架支持多线程编程,接口定义在 :
/Xcode3.1.4/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSThread.h
到相应的目录下打开此文件,可以看到绝大多数 java 中的接口这里也都能找到相应的实现,如下:
/* NSThread.h
Copyright (c) 1994-2007, Apple Inc. All rights reserved.
*/
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
@class NSArray, NSMutableDictionary, NSDate;
@interface NSThread : NSObject {
@private
id _private;
uint8_t _bytes[44];
}
+ (NSThread *)currentThread;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
+ (BOOL)isMultiThreaded;
- (NSMutableDictionary *)threadDictionary;
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
+ (void)exit;
#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
#endif
#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (NSArray *)callStackReturnAddresses;
- (void)setName:(NSString *)n;
- (NSString *)name;
- (NSUInteger)stackSize;
- (void)setStackSize:(NSUInteger)s;
- (BOOL)isMainThread;
+ (BOOL)isMainThread; // reports whether current thread is main
+ (NSThread *)mainThread;
- (id)init; // designated initializer
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
- (BOOL)isExecuting;
- (BOOL)isFinished;
- (BOOL)isCancelled;
- (void)cancel;
- (void)start;
- (void)main; // thread body method
#endif
@end
FOUNDATION_EXPORT NSString * const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSString * const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSString * const NSThreadWillExitNotification;
@interface NSObject (NSThreadPerformAdditions)
#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
#endif
#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
#endif
@end
从接口的定义中可以知道, NSThread 和大多数 iphone 的接口对象一样,有两种方式可以初始化:
一种使用 initWithTarget :(id)target selector:(SEL)selector object:(id)argument, 但需要负责在对象的 retain
count为 0 时调用对象的 release 方法清理对象。
另一种则使用所谓的 convenient method, 这个方便接口就是 detachNewThreadSelector, 这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。
因为在笔者的 iphone 版本“售票系统多线程”程序中需要设置线程的诸多参数,所以需要采用第一种方法来生成线程对象并自己启动它们。
首先,新建一个“ Window-based Application ”项目,并命名为 SellTickets ,接下来在 SellTicketsAppDelegate.h 文件中声明以下变量:
//
// SellTicketsAppDelegate.h
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
笔者在头文件中声明了两个 NSThread 的指针,下面需要在 *.m 文件中初始化并实现它们,如下:
//
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread
currentThread] name]);
tickets--;
}else
{
break;
}
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[window release];
[super dealloc];
}
@end
笔者在实现中用 alloc 初始化了两个 NSThread 对象并分别把它们命名为“ Thread-1 ”和“ Thread-2 ”,运行程序可以看到如下输出:
[Session started at 2009-11-10 14:25:26 +0800.]
2009-11-10 14:25:28.814 SellTickets[1298:4103] 当前票数是 :100, 售出 :0, 线程名 :Thread-1
2009-11-10 14:25:28.814 SellTickets[1298:4203] 当前票数是 :100, 售出 :0, 线程名 :Thread-2
2009-11-10 14:25:29.316 SellTickets[1298:4103] 当前票数是 :98, 售出 :2, 线程名 :Thread-1
2009-11-10 14:25:29.316 SellTickets[1298:4203] 当前票数是 :98, 售出 :2, 线程名 :Thread-2
2009-11-10 14:25:29.817 SellTickets[1298:4103] 当前票数是 :96, 售出 :4, 线程名 :Thread-1
2009-11-10 14:25:29.817 SellTickets[1298:4203] 当前票数是 :96, 售出 :4, 线程名 :Thread-2
2009-11-10 14:25:30.318 SellTickets[1298:4103] 当前票数是 :94, 售出 :6, 线程名 :Thread-1
2009-11-10 14:25:30.318 SellTickets[1298:4203] 当前票数是 :94, 售出 :6, 线程名 :Thread-2
2009-11-10 14:25:30.819 SellTickets[1298:4103] 当前票数是 :92, 售出 :8, 线程名 :Thread-1
2009-11-10 14:25:30.819 SellTickets[1298:4203] 当前票数是 :92, 售出 :8, 线程名 :Thread-2
2009-11-10 14:25:31.320 SellTickets[1298:4103] 当前票数是 :90, 售出 :10, 线程名 :Thread-1
2009-11-10 14:25:31.320 SellTickets[1298:4203] 当前票数是 :90, 售出 :10, 线程名 :Thread-2
2009-11-10 14:25:31.820 SellTickets[1298:4103] 当前票数是 :88, 售出 :12, 线程名 :Thread-1
2009-11-10 14:25:31.821 SellTickets[1298:4203] 当前票数是 :87, 售出 :13, 线程名 :Thread-2
2009-11-10 14:25:32.321 SellTickets[1298:4103] 当前票数是 :86, 售出 :14, 线程名 :Thread-1
2009-11-10 14:25:32.322 SellTickets[1298:4203] 当前票数是 :86, 售出 :14, 线程名 :Thread-2
2009-11-10 14:25:32.823 SellTickets[1298:4103] 当前票数是 :84, 售出 :16, 线程名 :Thread-1
2009-11-10 14:25:32.823 SellTickets[1298:4203] 当前票数是 :83, 售出 :17, 线程名 :Thread-2
2009-11-10 14:25:33.323 SellTickets[1298:4103] 当前票数是 :82, 售出 :18, 线程名 :Thread-1
2009-11-10 14:25:33.324 SellTickets[1298:4203] 当前票数是 :81, 售出 :19, 线程名 :Thread-2
2009-11-10 14:25:33.824 SellTickets[1298:4103] 当前票数是 :80, 售出 :20, 线程名 :Thread-1
2009-11-10 14:25:33.825 SellTickets[1298:4203] 当前票数是 :79, 售出 :21, 线程名 :Thread-2
2009-11-10 14:25:34.325 SellTickets[1298:4103] 当前票数是 :78, 售出 :22, 线程名 :Thread-1
2009-11-10 14:25:34.326 SellTickets[1298:4203] 当前票数是 :77, 售出 :23, 线程名 :Thread-2
2009-11-10 14:25:34.826 SellTickets[1298:4103] 当前票数是 :76, 售出 :24, 线程名 :Thread-1
2009-11-10 14:25:34.827 SellTickets[1298:4203] 当前票数是 :75, 售出 :25, 线程名 :Thread-2
2009-11-10 14:25:35.327 SellTickets[1298:4103] 当前票数是 :74, 售出 :26, 线程名 :Thread-1
2009-11-10 14:25:35.328 SellTickets[1298:4203] 当前票数是 :73, 售出 :27, 线程名 :Thread-2
2009-11-10 14:25:35.827 SellTickets[1298:4103] 当前票数是 :72, 售出 :28, 线程名 :Thread-1
2009-11-10 14:25:35.830 SellTickets[1298:4203] 当前票数是 :71, 售出 :29, 线程名 :Thread-2
2009-11-10 14:25:36.329 SellTickets[1298:4103] 当前票数是 :70, 售出 :30, 线程名 :Thread-1
2009-11-10 14:25:36.330 SellTickets[1298:4203] 当前票数是 :69, 售出 :31, 线程名 :Thread-2
2009-11-10 14:25:36.830 SellTickets[1298:4103] 当前票数是 :68, 售出 :32, 线程名 :Thread-1
2009-11-10 14:25:36.831 SellTickets[1298:4203] 当前票数是 :67, 售出 :33, 线程名 :Thread-2
2009-11-10 14:25:37.331 SellTickets[1298:4103] 当前票数是 :66, 售出 :34, 线程名 :Thread-1
2009-11-10 14:25:37.332 SellTickets[1298:4203] 当前票数是 :65, 售出 :35, 线程名 :Thread-2
2009-11-10 14:25:37.832 SellTickets[1298:4103] 当前票数是 :64, 售出 :36, 线程名 :Thread-1
2009-11-10 14:25:37.833 SellTickets[1298:4203] 当前票数是 :63, 售出 :37, 线程名 :Thread-2
2009-11-10 14:25:38.333 SellTickets[1298:4103] 当前票数是 :62, 售出 :38, 线程名 :Thread-1
2009-11-10 14:25:38.334 SellTickets[1298:4203] 当前票数是 :61, 售出 :39, 线程名 :Thread-2
2009-11-10 14:25:38.834 SellTickets[1298:4103] 当前票数是 :60, 售出 :40, 线程名 :Thread-1
2009-11-10 14:25:38.836 SellTickets[1298:4203] 当前票数是 :59, 售出 :41, 线程名 :Thread-2
2009-11-10 14:25:39.335 SellTickets[1298:4103] 当前票数是 :58, 售出 :42, 线程名 :Thread-1
2009-11-10 14:25:39.337 SellTickets[1298:4203] 当前票数是 :58, 售出 :42, 线程名 :Thread-2
2009-11-10 14:25:39.838 SellTickets[1298:4103] 当前票数是 :56, 售出 :44, 线程名 :Thread-1
2009-11-10 14:25:39.839 SellTickets[1298:4203] 当前票数是 :55, 售出 :45, 线程名 :Thread-2
2009-11-10 14:25:40.339 SellTickets[1298:4103] 当前票数是 :54, 售出 :46, 线程名 :Thread-1
2009-11-10 14:25:40.340 SellTickets[1298:4203] 当前票数是 :53, 售出 :47, 线程名 :Thread-2
2009-11-10 14:25:40.840 SellTickets[1298:4103] 当前票数是 :52, 售出 :48, 线程名 :Thread-1
2009-11-10 14:25:40.841 SellTickets[1298:4203] 当前票数是 :51, 售出 :49, 线程名 :Thread-2
2009-11-10 14:25:41.341 SellTickets[1298:4103] 当前票数是 :50, 售出 :50, 线程名 :Thread-1
2009-11-10 14:25:41.342 SellTickets[1298:4203] 当前票数是 :49, 售出 :51, 线程名 :Thread-2
2009-11-10 14:25:41.842 SellTickets[1298:4103] 当前票数是 :48, 售出 :52, 线程名 :Thread-1
2009-11-10 14:25:41.843 SellTickets[1298:4203] 当前票数是 :47, 售出 :53, 线程名 :Thread-2
2009-11-10 14:25:42.343 SellTickets[1298:4103] 当前票数是 :46, 售出 :54, 线程名 :Thread-1
2009-11-10 14:25:42.344 SellTickets[1298:4203] 当前票数是 :45, 售出 :55, 线程名 :Thread-2
2009-11-10 14:25:42.844 SellTickets[1298:4103] 当前票数是 :44, 售出 :56, 线程名 :Thread-1
2009-11-10 14:25:42.845 SellTickets[1298:4203] 当前票数是 :43, 售出 :57, 线程名 :Thread-2
2009-11-10 14:25:43.345 SellTickets[1298:4103] 当前票数是 :42, 售出 :58, 线程名 :Thread-1
2009-11-10 14:25:43.346 SellTickets[1298:4203] 当前票数是 :42, 售出 :58, 线程名 :Thread-2
2009-11-10 14:25:43.846 SellTickets[1298:4103] 当前票数是 :40, 售出 :60, 线程名 :Thread-1
2009-11-10 14:25:43.847 SellTickets[1298:4203] 当前票数是 :39, 售出 :61, 线程名 :Thread-2
2009-11-10 14:25:44.347 SellTickets[1298:4103] 当前票数是 :38, 售出 :62, 线程名 :Thread-1
2009-11-10 14:25:44.348 SellTickets[1298:4203] 当前票数是 :37, 售出 :63, 线程名 :Thread-2
2009-11-10 14:25:44.848 SellTickets[1298:4103] 当前票数是 :36, 售出 :64, 线程名 :Thread-1
2009-11-10 14:25:44.849 SellTickets[1298:4203] 当前票数是 :35, 售出 :65, 线程名 :Thread-2
2009-11-10 14:25:45.349 SellTickets[1298:4103] 当前票数是 :34, 售出 :66, 线程名 :Thread-1
2009-11-10 14:25:45.350 SellTickets[1298:4203] 当前票数是 :33, 售出 :67, 线程名 :Thread-2
2009-11-10 14:25:45.850 SellTickets[1298:4103] 当前票数是 :32, 售出 :68, 线程名 :Thread-1
2009-11-10 14:25:45.851 SellTickets[1298:4203] 当前票数是 :31, 售出 :69, 线程名 :Thread-2
2009-11-10 14:25:46.350 SellTickets[1298:4103] 当前票数是 :30, 售出 :70, 线程名 :Thread-1
2009-11-10 14:25:46.351 SellTickets[1298:4203] 当前票数是 :29, 售出 :71, 线程名 :Thread-2
2009-11-10 14:25:46.851 SellTickets[1298:4103] 当前票数是 :28, 售出 :72, 线程名 :Thread-1
2009-11-10 14:25:46.853 SellTickets[1298:4203] 当前票数是 :27, 售出 :73, 线程名 :Thread-2
2009-11-10 14:25:47.352 SellTickets[1298:4103] 当前票数是 :26, 售出 :74, 线程名 :Thread-1
2009-11-10 14:25:47.354 SellTickets[1298:4203] 当前票数是 :25, 售出 :75, 线程名 :Thread-2
可以看到,因为两个线程共享变量 tickets 和 count ,开头的输出就产生了异常情况, iphone 虽然没有提供类似 java 下的synchronized 关键字,但提供了 NSCondition 对象接口。查看 NSCondition 的接口说明可以看出, NSCondition 是 iphone 下的锁对象,所以我们需要让代码成为线程安全的,修改头文件如下:
//
// SellTicketsAppDelegate.h
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
然后在实现中添加如下代码:
//
// SellTicketsAppDelegate.m
// SellTickets
//
// Created by sun dfsun2009 on 09-11-10.
// Copyright __MyCompanyName__ 2009. All rights reserved.
//
#import "SellTicketsAppDelegate.h"
@implementation SellTicketsAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
tickets = 100;
count = 0;
//
锁对象
ticketCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
//[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)run{
while (TRUE) {
//
上锁
[ticketsCondition lock];
if(tickets > 0)
{
[NSThread sleepForTimeInterval:0.5];
count = 100 - tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread
currentThread] name]);
tickets--;
}else
{
break;
}
[ticketsCondition unlock];
}
}
- (void)dealloc {
[ticketsThreadone release];
[ticketsThreadtwo release];
[ticketsCondition release];
[window release];
[super dealloc];
}
@end
最后千万别忘记在 dealloc 方法中调用对象的 release 进行资源释放,现在再次运行下看看, iphone 版本的“售票系统多线程”程序是否跑起来了:)
相关文章推荐
- 利用iphone的多线程实现“售票系统”(手把手教你iphone开发 - 基础篇)
- 利用iphone的多线程实现“售票系统”(手把手教你iphone开发 - 基础篇)
- 利用iphone的多线程实现“售票系统”
- 【iphone游戏开发】Iphone游戏开发之五:游戏场景切换,点阵字的实现和Hiero工具的利用
- 多线程火车票售票系统——自动重置事件对象实现线程同步
- 玩转iPhone网络通讯之BSD Socket篇(手把手教你iphone开发 - 基础篇)
- 深入理解iPhone静态库(手把手教你iphone开发 - 基础篇)
- 深入理解iPhone数据持久化(手把手教你iphone开发 – 基础篇)
- 利用阿里云OSS开发一个私人网盘/外链系统,php+js实现
- 深入理解iPhone静态库(手把手教你iphone开发 - 基础篇)
- 深入理解iPhone委托模式兼谈iPhone生命周期(手把手教你iphone开发 - 基础篇)
- C# ThreadPool 自定义线程管理池 实现多线程池管理 有助与开发多线程系统的线程运行情况监控。
- iOS开发 ( iPhone/iPad):利用ffmpeg 实现音频解码、声音播放
- 深入理解iPhone屏幕双缓冲技术(手把手教你iphone开发 - 基础篇)
- iOS开发利用系统推送Notifaction和轮询实现简单聊天系统
- 【iphone游戏开发】Iphone游戏开发之五:游戏场景切换,点阵字的实现和Hiero工具的利用
- [Java]利用java.util.concurrent实现多线程的线程池开发
- SSH开发框架中,实现系统启动加载类,读取数据库常用数据进入内存,利用Spring托管,并完成reload功能
- 本文是笔者根据数据库编程经验,利用C++语言的模板、继承、授权、多态等面向对象特性,借鉴命令模式,实现了对象在关系数据中的存储,降低应用系统与数据库之间的耦合,提高开发效率。
- iphone开发使用NSThread和NSInvocationOperation实现多线程