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?
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.