Can someone help,
I have a NSMutablearray which contains approx a 1000 data entries.
What i want to do is go though these entries one by one and send them to a server with HTTP request.
However its important that i only send them one at a time, so i have the following pseudo code
for(int i =0; i < array.count; i++)
{
[ServerLayer ServerUploadRow:[array objectAtIndex:i] : ^void (int ReturnValue, BOOL err){
if(err == true)
{
//Do some local stuff here.
}
}];
}
what i ideally want to do is having something like the following
for(int i =0; i < array.count; i++)
{
//Wait here till i know that we not waiting on a block to complete
[ServerLayer ServerUploadRow:[array objectAtIndex:i] : ^void (int ReturnValue, BOOL err){
if(err == true)
{
//Do some local stuff here.
//Signify that we done
}
}];
}
the function ServerUploadRow just sends HTTP command.
Thanks
Assuming that ServerUploadRow runs asynchronously, you can have the request initiate the next request in the completion block of the previous one:
- (void)initiateRequestNumber:(NSInteger)index
{
[ServerLayer ServerUploadRow:array[index] : ^(int ReturnValue, BOOL err){
if (err == true) {
//Do some local stuff here.
} else {
NSInteger nextIndex = index + 1;
if (nextIndex < array.count) {
[self initiateRequestNumber:nextIndex];
} else {
// do whatever you want now that everything is done
}
}
}];
}
And you'd start this with:
[self initiateRequestNumber:0];
If you can issue these requests concurrently, or better combine all of this in one request, it will be much faster. I'd suggest considering refactoring your web service to enable this, if at all possible. But submitting 1000 network requests sequentially is going to horribly slow (as you'll suffer network latency 1000 times).
Related
I'm parsing midi messages inside an autoreleasepool. After determining the message that's being sent, I make calls to the main thread using dispatch_async(dispatch_get_main_queue(). This seems to work well most of the time but occasionally I'll get a crash where it stops on the line
while (semaphore_wait(midiReceivedSemaphore) == KERN_SUCCESS ) {
The method looks something like this:
#autoreleasepool {
unsigned int maxPacketLength = 0x100;
unsigned char* data = malloc(maxPacketLength);
UInt16 length;
while (semaphore_wait(midiReceivedSemaphore) == KERN_SUCCESS ) {
midi_packet_buffer_next_packet_length(midiPacketBuffer, &length);
if( length > 0) {
length = midi_packet_buffer_read(midiPacketBuffer, data, length);
for (unsigned int packetContentsIndex = 0; packetContentsIndex < length; packetContentsIndex++) {
while (packetContentsIndex < length) {
Byte command = data[packetContentsIndex];
switch (command) {
case 0xA0: //aftertouch
commandLength += 2;
int meterChannel = data[packetContentsIndex +1];
int meterValue = data[packetContentsIndex +2];
if (meterValue < 0x10)
{
dispatch_async(dispatch_get_main_queue(), ^{
[delegate sendLevelMeterPT:meterChannel withValue:meterValue ];
});
}
break;
default:
break;
}
}
}
}
}
free(data);
}
Is this a very bad way of handling the MIDI messages? The reason I used dispatch_async is so that it wouldn't slow down the midi processing thread, before using dispatch_async the method was missing some messages if they were coming in fast. Now it catches all the messages but I get the occasional crash. I'm thinking I might have to rewrite the whole midi processing method but want to know the proper way to go about it. Any advice would be great.
I'm writing a Cocoa app that does sometimes long calculations. I'd like to be able to abort those calculations with a cmd-key sequence. I start the calculations from a command line that is implemented in a subclass of NSTextView. I've over ridden keyDown and get events there, but only when the calculation finishes. I can't figure out how to have the long calculation force an event check periodically that would cause keyDown to be called so I can set a flag to abort the calculation. Seems like there may be an easy way, but I can't seem to find it.
Without worrying about background threads, I was hoping for something like this:
//commandView is a subclass of NSTextView
commandView* theCommands;
extern int continueCalc;
#implementation commandView
- (void)keyDown:(NSEvent *)anEvent{
static int first = 1;
if(first){
theCommands = self;
first = 0;
}
NSString *theKey = [anEvent characters];
[super keyDown:anEvent];
if([theKey isEqualToString:#"s"]){
NSLog(#"start 10 second calc");
continueCalc = 1;
doCalc(10);
} else if ([theKey isEqualToString:#"x"]){
NSLog(#"stop calc");
continueCalc = 0;
}
}
- (void)checkEvent{
NSLog(#"would like to force an event check so that typing x would stop the calc");
}
#end
// and the separate calculation code in another file:
int continueCalc = 1;
extern commandView* theCommands;
void doCalc(int n){
clock_t start;
for (int i=0; i<n && continueCalc; i++) {
start = clock();
while ( (clock()- start)*60/CLOCKS_PER_SEC < 60); //wait a second
// something here to check an event
[theCommands checkEvent];
}
}
One way to do this properly is with NSThread. You can very easily create a new thread which just calls your calculation method. In your calculation method, you check a variable to see if it's been aborted. Meanwhile on the main thread, the UI remains responsive and you can use an event (a key press or button press) to set the variable which is checked on the other thread. I might look something like this:
- (void)startCalculations
{
[NSThread detachNewThreadSelector:#selector (runCalculations)
toTarget:myObject
withObject:nil]; // Or you can send an object if you need to
}
Then in the code for myObject:
- (void)runCalculations
{
for (int i = 0; (i < maxWhatever) && (!stopCalculations); i++)
{
... do one iteration of your calculation
}
}
Then when your UI code gets the proper key or button press, you simply tell myObject to set stopCalculations to YES.
[myObject setStopCalculations:YES];
I should note that there are other ways to do this, like using GCD or pthreads directly, but this is a very simple way to just run a single method on another thread, and all the details of starting and tearing down the thread are taken care of for you.
The answer above works perfectly and showed me how to approach threads, which I hadn't used before. Thanks for that. With that working, I wanted the calculation to be able to report progress back to the command decoder (NSTextView). Doing that in the new thread caused problems, so I looked into using GCD. The implementation below seems to work, but this is my first GCD code and I would welcome any suggestions or caveats.
// CommandView is a subclass of NSTextView
commandView* theCommands;
extern int continueCalc;
#implementation commandView
- (void)keyDown:(NSEvent *)anEvent{
static int first = 1;
if(first){
theCommands = self;
}
NSString *theKey = [anEvent characters];
if([theKey isEqualToString:#"s"]){
[self appendText:#"Start\n"];
[self startCalculations];
return;
} else if ([theKey isEqualToString:#"x"]){
[self appendText:#"\nStop"];
continueCalc = 0;
return;
}
[super keyDown:anEvent];
}
- (void)startCalculations
{
void doCalc(int);
continueCalc = 1;
dispatch_queue_t queue = dispatch_queue_create("oma.oma2.CommandTask",0);
dispatch_async(queue,^{
for (int i=0; i<10 && continueCalc; i++) {
doCalc(0);
NSLog(#"%d",i);
}
});
}
-(void) appendText:(NSString *) string{
[self.textStorage.mutableString appendString:string];
}
#end
// the separate calculation code in a different file
#import "commandView.h"
extern commandView* theCommands;
int continueCalc = 1;
void doCalc(int n){
clock_t start;
start = clock();
while ( (clock()- start)*60/CLOCKS_PER_SEC < 60); //wait a second
// print progress in the main thread
dispatch_sync(dispatch_get_main_queue(),^{[theCommands appendText:#"."];});
}
In my app I'me getting responses from the server and I have to check that I don't create duplicate objects in the NSArray which contains NSDictionaries. Now to check if the objects exists I do this:
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
}
if (i == appDelegate.currentUser.userSiteDetailsArray.count - 1 && ![[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier] && needToCheck){
// It means it's the last object we've iterated through and needToCheck is still = YES;
//Doing stuff here
}
}
I set up a BOOL value because this iteration goes numerous times inside a method and I can't use return to stop it. I think there is a better way to perform this check and I would like to hear your suggestions about it.
BOOL needToCheck = YES;
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
break;
}
}
if (needToCheck) {
//Doing stuff here
}
But, as others have said, you can maybe keep a "summary" in a separate NSSet that you check first, vs spinning through all the dictionaries.
NSDictionary *previousThing = nil;
for (NSDictionary *thing in appDelegate.currentUser.userSiteDetailsArray) {
if ([thing[#"webpropID"] isEqualToString:newWebPropertyIdentifier]) {
previousThing = thing;
break;
}
}
if (previousThing == nil) {
// no previous thing
} else {
// duplicate
}
As you may remember, I am trying to use GCD to speed up some of my code, namely a collision detection and resolution engine. However, I am clearly doing something wrong because all of my GCD code is significantly slower and less consistent than my serial code (between 1.4x and 10x slower). Allow me to give you an example: I am iterating over an array in a bubble-sort fashion to determine all possible collisions among objects in that array:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
}
}
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
Pretty straightforward, and it seems to perform well given the constraints of the problem. However, I would like to take advantage of the fact that the state of each object is not modified in the code section and use GCD to parallelize this work. To do this I am trying something like this:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
NSBlockOperation* blockOperation = nil;
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
void (^workBlock) (void) = ^()
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
};
if (!blockOperation)
{
blockOperation = [NSBlockOperation blockOperationWithBlock:b];
}
else
{
[blockOperation addExecutionBlock:workBlock];
}
}
}
[opQueue addOperation:blockOperation];
[opQueue autorelease];
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
Can anyone help to put me on the right track and perhaps provide a link to a good GCD tutorial? I have looked over several GCD tutorials and scoured all of the documentation and I still feel that my grasp on the subject is tenuous at best. Thanks!
Is there a reason you're not using the GCD C API and the dispatch_* family of functions? You don't have much control over the GCD aspects of NSOperationQueue (like which queue you want to submit the blocks to). Also, I can't tell if you're using iOS or not, but NSOperationQueue does not use GCD on iOS. That might be the reason it spawned so many threads. Either way, your code will be shorter and simpler if you use the GCD API directly:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < count; i++)
{
dispatch_group_async(group, queue, ^{
for (int j = i + 1; j < count; j++)
{
dispatch_group_async(group, queue, ^{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
});
}
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
You can use a dispatch_group to group all of the executions together and wait for them all to finish with dispatch_group_wait. If you don't care to know when the the blocks finish, you can ignore the group part and just use dispatch_async. The dispatch_get_global_queue function will get one of the 3 concurrent queues (low, default or high priority) for you to submit your blocks to. You shouldn't have to worry about limiting the thread count or anything like that. The GCD scheduler is supposed to do all of that for you. Just make sure you submit to a concurrent queue, which could either be one of the 3 global queues, or a queue you've created by passing DISPATCH_QUEUE_CONCURRENT to dispatch_queue_create (this is available starting OS X 10.7 and iOS 5.0).
If you're doing some file I/O in each block or taxing some other resource, you might need to reign in GCD and limit the number of blocks you're submitting to the queue at once. This will have the same effect as limiting the concurrent operation count in an NSOperationQueue. You can use a GCD semaphore to do this:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < count; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
for (int j = i + 1; j < count; j++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
dispatch_semaphore_signal(semaphore);
});
}
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(semaphore);
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
Once you get the hang of it, GCD is very simple to use. I use it all over my code now.
Can anyone help to put me on the right track and perhaps provide a link to a good GCD tutorial?
Run, don't walk over to Mike Ash's blog. His series on GCD is the clearest and most concise I've seen, and it'll only take you around 30 minutes to read the whole thing. Apple's WWDC videos from 2010 on GCD And blocks are also pretty good.
In your code you are delaying the work you need to do for each object until the end of the nested for loop. That said, when the loop finishes you will have one operation with lots of blocks for a bunch of objects and you won't thereby take advantage of the GCD properly.
I would suggest you create one NSBlockOperation for each object and add it to the NSOperationQueue in the end of each for (int j = i + 1; j < count; j++) iteration.
This way, the system will begin processing the work you need to do for each object as soon as the iteration ends.
Also keep in mind that the queue shouldn't be much larger than the available processors, otherwise you will have some overhead on the thread switch process that will compromise speed.
Ok, basically I have a run loop going in my application every second or two, while at the same time I have another thread going that is looping through the listenForPackets method; broadcastMessage is only initiated when another action method takes place. The important part of this question is that when the listener thread is running seperately from the main thread, it never print out any of the print commands and it seems to not allow access to a global variable that I have defined called recvMessage which lies outside of the interface and implementation sections.
In my code, I have it set up so that every time it runs through the main run loop, it updates a UILabel in my GUI. When the app is running, my label stays blank the whole time and never changes. I've double-checked the GUI and everything is linked up there correctly and my label is instantiated correctly too (I use the name "label" as an instance of UILabel in the code below). Does anyone have any ideas why my label is updating? The networking aspect of things is fine I believe, because I just got that done and everything is "talking" ok. Maybe it's a variable scope issue that I don't know about, or are separate threads allowed to access global variables, such as the one I have used below (rcvMessage)? I'm fairly new to multi-threaded applications but I don't believe it's really that hard to implement, using NSThread (only one line of code).
Global Variable
NSString *recvMessage;
Main Runloop - the section that updates the label every time it goes through the runloop
if (label.text != recvMessage)
label.text = recvMessage
Talker Method
-(void)broadcastMessage { // (NSString*)msg {
msg = #"From_Master";
NSLog(#"broadcastMessage - Stage 1");
mc_ttl = 15; // number of node hops the message is allowed to travel across the network
// define the port we will be using
mc_port = MYPORT;
// create a socket for sending to the multicast address
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
NSLog(#"ERROR: broadcastMessage - socket() failed");
return;
}
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(GROUP_ADDRESS);
mc_addr.sin_port = htons(MYPORT);
NSLog(#"broadcastMessage - Stage 2");
// set the TTL (time to live/hop count) for the send
if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &mc_ttl, sizeof(mc_ttl))) < 0) {
NSLog(#"ERROR: broadcastMessage - setsockopt() failed");
return;
}
NSLog(#"broadcastMessage - Stage 3");
// clear send buffer
memset(send_str, 0, sizeof(send_str));
// convert the message to a C string to send
[msg getCString:send_str maxLength:MAX_LEN encoding:NSASCIIStringEncoding];
//while (fgets(send_str, MAX_LEN, stdin)) {
NSLog(#"broadcastMessage - Stage 4");
NSLog(#"Message =");
printf(send_str);
// send string to multicast address
if ((sendto(sock, send_str, sizeof(send_str), 0, (struct sockaddr *)&mc_addr, sizeof(mc_addr))) < 0) {
NSLog(#"ERROR: broadcastMessage - sendto() sent incorrect number of bytes");
//return;
}
NSLog(#"Sent Message -");
printf(send_str);
NSLog(#"broadcastMessage - Stage 5");
// clear send buffer
memset(send_str, 0, sizeof(send_str));
NSLog(#"broadcastMessage - Stage 6 - Complete");
close(sock);
}
Listener Method
-(void)listenForPackets {
listeningFlag_on = 1; // allows reuse of the same socket
NSLog(#"listenForPackets - Stage 1");
if ((listeningSock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP)) < 0) {
NSLog(#"ERROR: listenForPackets - socket() failed");
return;
// make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"listenForPackets - Stage 2");
// set reuse port to on to allow multiple binds per host
if ((setsockopt(listeningSock, SOL_SOCKET, SO_REUSEADDR, &listeningFlag_on, sizeof(listeningFlag_on))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() Reuse failed");
return;
// make the method return an int instead of void and use this statement to check for errors
}
// construct a multicast address structure after erasing anything in the listeningmc_addr structure
memset(&listeningmc_addr, 0, sizeof(listeningmc_addr));
listeningmc_addr.sin_family = AF_INET;
listeningmc_addr.sin_addr.s_addr = htonl(INADDR_ANY); // different from sender
listeningmc_addr.sin_port = htons(MYPORT);
// bind multicast address to socket
if ((bind(listeningSock, (struct sockaddr *)&listeningmc_addr, sizeof(listeningmc_addr))) < 0) {
NSLog(#"ERROR: listenForPackets - bind() failed");
perror("Bind() -");
return; // make the method return an int instead of void and use this statement to check for errors
}
//*********************************************************************************
NSString *ipAddress = [[NSString alloc] initWithString:self.getIPAddress];
const char *tmp = [ipAddress UTF8String];
listeningMc_addr_str = tmp;
printf("%s\n", listeningMc_addr_str);
listeningMc_req.imr_multiaddr.s_addr = inet_addr(GROUP_ADDRESS);
listeningMc_req.imr_interface.s_addr = htonl(INADDR_ANY);
// send an ADD MEMBERSHIP message via setsockopt
if ((setsockopt(listeningSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &listeningMc_req, sizeof(listeningMc_req))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() failed");
int err = errno;
NSLog(#"errno - %i", err);
NSLog(#"Error = %s", strerror(err));
perror("ERROR");
return; // make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"listenForPackets - Stage 3");
for (;;) { // loop forever
// clear the receive buffers & structs
memset(listeningRecv_str, 0, sizeof(listeningRecv_str));
listeningFrom_len = sizeof(listeningFrom_addr);
memset(&listeningFrom_addr, 0, listeningFrom_len);
NSLog(#"Test #1 Complete");
//msgStatus.text = #"Waiting...";
// block waiting to receive a packet
listeningFrom_len = sizeof(listeningmc_addr);
if ((listeningRecv_len = recvfrom(listeningSock, listeningRecv_str, MAX_LEN, 0, (struct sockaddr*)&listeningmc_addr, &listeningFrom_len)) < 0) {
NSLog(#"ERROR: listenForPackets - recvfrom() failed");
return; // make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"Test #2 Complete - Received a Message =");
NSLog(#"listenForPackets - Stage 4");
// listeningRecv_str
**tmpString = [[NSString alloc] initWithCString:listeningRecv_str encoding:NSASCIIStringEncoding];
NSLog(#"Message Received =");
NSLog(tmpString);
recvMessage = tmpString;**
//}
// received string
printf("Received %d bytes from %s: ", listeningRecv_len, inet_ntoa(listeningFrom_addr.sin_addr));
printf("%s", listeningRecv_str);
//}
}
// send a DROP MEMBERSHIP message via setsockopt
if ((setsockopt(listeningSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &listeningMc_req, sizeof(listeningMc_req))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() drop membership failed");
//return 1; // make the method return an int instead of void and use this statement to check for errors
}
close(listeningSock);
NSLog(#"listenForPackets - Stage 5 - Complete");
}
Yes, all threads can access global variables. There are certainly problems with how you are using the global--you leak the NSString you create every time the variable is updated, and you are accessing the same memory from two threads without any access control--but there's nothing that would prevent the variable from being updated.
If none of your log messages are being printed, the problem is that the code is never being run, which is why the variable isn't changing. You should take a look at the code that is supposed to kick off this new thread.
Also note that updating any UI components, you need to use the method "performSelectorOnMainThread" to do any value setting of label text or any other GUI elements. The value will not update from background threads.