Question

Sorry about the vague title of this question, I couldn't think of a concise sentence to explain the problem I'm having.

Basically I want to start multiple threads and pass an object with thread-specific values that I can use within that context. Here is the code that does that

while (count < no_threads) {
    _thread_bytes = _file_length / no_threads;
    _thread_offset = count * _thread_bytes;

    NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithInt:_thread_offset],@"thread_offset",
                          [NSNumber numberWithInt:_thread_bytes], @"thread_bytes",
                          [NSNumber numberWithInt:count], @"count", nil];

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(processServerThread:) object:args];
    [thread start];
    count++;
}

Each thread calls the selector method with the correct thread_bytes and thread_offset and also has access to a GCDAsyncSocket socket, this consequently results in many delegate method calls that use the thread_bytes and thread_offset. Here is the selector method.

- (void)processServerThread:(id)sender
{    
    NSError *err;

    dispatch_queue_t iQueue = dispatch_queue_create("main_server_queue", NULL);

    GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:iQueue];

    if (![socket connectToHost:@"example.com" onPort:8080 error:&err]) {

        NSLog(@"Error: couldn't connect to file server....");

        return;
    }

    NSData *message =[[self requestMessageWithOffset:[NSNumber numberWithInt:_thread_offset]
                            andBytes:[NSNumber numberWithInt:_thread_bytes]]
                            dataUsingEncoding:NSUTF8StringEncoding];

    [socket writeData:message withTimeout:SOCKET_TIMEOUT tag:TAG_FILE_REQUEST_WRITE];
}

If there are N threads being processed, when I send the request message the value of _thread_offset, for example if offsets are 0, 2, 4, 8. It shows 0 for one thread an 8 for all other threads, while they should have each a unique offset within that context.

Also, is there any callback method for when a thread finishes?

Was it helpful?

Solution

It's unclear to me why you're creating these threads at all. The entire point behind GCDAsyncSocket is to do asynchronous socket communications for you. It seems unnecessary to create your own threads, have each launch asynchronous write tasks.

In fact, as I read your code sample, your threads will terminate as soon as the asynchronous write has been initiated (but likely before the write is finished), which I do not think was your intent.

Setting that aside, you ask:

It shows 0 for one thread an 8 for all other threads, while they should have each a unique offset within that context.

As sahara108 pointed out, you're referencing the instance variables _thread_offset and _thread_bytes rather than accessing the values that you added to the dictionary you passed to your thread. You should retrieve these values from the dictionary you passed to the thread.

Also, is there any callback method for when a thread finishes?

You can observe NSThreadWillExitNotification (and obviously write a willEndThread method):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEndThread:) name:NSThreadWillExitNotification object:nil];

You obviously have to write a willEndThread method, too.

But you've specified self as the delegate for GCDAsyncSocket, so if you're looking for the completion of the write requests, you should implement those delegate methods.


Glancing at the GCDAsyncSocket source, it looks like they want you to use a serial queue. So, if you want to have concurrent requests going, you might try something like the following:

_thread_bytes = _file_length / no_threads;
for (int i = 0; i < no_threads; i++) {
    _thread_offset = i * _thread_bytes;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL success = [self processRequestWithOffset:_thread_offset byteCount:_thread_bytes];

        if (!success) {
            // do here whatever you want if the connection was unsuccessful
        }
    }
}

where

- (BOOL)processRequestWithOffset:(NSInteger)offset byteCount:(NSInteger)byteCount
{    
    NSError *error;

    dispatch_queue_t queue = dispatch_queue_create("main_server_queue", NULL);

    GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:queue];

    if (![socket connectToHost:@"example.com" onPort:8080 error:&error]) {    
        NSLog(@"Error: couldn't connect to file server: %@", error);
        return NO;
    }

    NSData *message = [[self requestMessageWithOffset:@(offset)
                                             andBytes:@(byteCount)
                                    dataUsingEncoding:NSUTF8StringEncoding];

    [socket writeData:message withTimeout:SOCKET_TIMEOUT tag:TAG_FILE_REQUEST_WRITE];

    return YES;
}

And if you want to know if each of the connections finished successfully, just implement the relevant GCDAsyncSocket delegate methods.

Personally, I'll be interested in seeing if this is any faster than doing a normal post of the data with pipelining turned on. I'm also not terribly familiar with the idiosyncrasies of GCDAsyncSocket, so I don't know if there are deeper reasons why they don't want you to use a concurrent queue, but the above might be a way to create multiple instances of GCDAsyncSocket without delving into NSThread (GCD or operation queues are generally preferable to NSThread; see Migrating Away From Threads in the Concurrency Programming Guide).

One final observation. I know that with NSURLConnection and NSURLSession that there is a limit as to how many concurrent network requests you can do (generally 4 or 5), so you might be wary of using too many GCDAsyncSocket instances, too.

OTHER TIPS

The problem is parameter of your requestMessageWithOffset:. You should use sender[@"thread_offset"] instead of [NSNumber numberWithInt:_thread_offset] . Also for thread_bytes. For the callback you should look up in GCDAsyncSocket protocol

Now use GCD to achieve this, have a look on below

Step-1: Create the Queue using dispatch_queue_create Step-2: Add the Block and call dispatch_async

dispatch_queue_t myQueue = dispatch_queue_create("My Queue", NULL);

dispatch_async(myQueue, ^{
    NSLog(@"Running code in secondary thread...");

    int value = 0;
    for (int i=0; i<100; i++) {
        for (int j=0; j<100; j++) {
            for (int n=0; n<100; n++) {
                value += j;
            }
        }
    }
    NSLog(@"From secondary thread: value = %d", value);
}); 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top