如何同时处理三个请求正确创建并发队列

How to create concurrent queues properly with three requests at the same time

我想同时获取三个请求以提高性能。 这是我当前的代码,它 returns 在我的 tableview 中只有一个平台:

 dispatch_queue_t concurrentQueue = dispatch_queue_create("com.myapp.myqueue", DISPATCH_QUEUE_CONCURRENT);

void(^block_readPS4)() = ^{
    self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:@"ps4"]];
};
void(^block_readXONE)() = ^{
    self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:@"xboxone"]];
};
void(^block_readPC)() = ^{
    self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:@"pc"]];
};
void(^block_write)() = ^{dictionaryWithDictionary:self.releasesDict];
    self.releasesArr = [self.releasesDict allKeys];
    [self.tableView reloadData];
    [self.activityInd stopAnimating];
};
dispatch_async(concurrentQueue,block_readPS4);
dispatch_async(concurrentQueue,block_readXONE);
dispatch_async(concurrentQueue,block_readPC);

dispatch_barrier_async(concurrentQueue, block_write);

我知道问题出在self.releasesDict,如何改进?

您在每次阅读时都会覆盖 self.releasesDict。因此,在您的写入块中, self.releasesDict 的值将是系统执行的最后一次读取。如果你想在同一个 NSDictionary 中读取所有内容,请将 self.releasesDict 声明为 NSMutableDictionary 并使用 :

初始化它
self.releasesDict = [NSMutableDictionary dictionary];

在您的阅读中:

[self.releasesDict addEntriesFromDictionary:[NSDictionary dictionaryWithDictionary:/*what you want*/]];

@ryancrunchi 是正确的:

You're overwriting self.releasesDict in each read. So in your write block the value of self.releasesDict will be the last read that the system performed.

...但他提出的解决方案不能保护字典免受并发写入,并且 NSMutableDictionary 本质上不是线程安全的。您必须保护它免受并发读取 + 写入 and/or 写入 + 写入。这是您可以这样做的一种方法(在评论中详细介绍了所做的一切):

// A local (non-shared) mutable dictionary. Using a local dictionary allows us to know that no one
// else is reading from or writing to it concurrently, which is not guaranteed if we use self.releasesDict
NSMutableDictionary* localDict = [NSMutableDictionary dictionary];

// Your original concurrent queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.myapp.myqueue", DISPATCH_QUEUE_CONCURRENT);

// A serial queue to protect localDict from reading and writing.
dispatch_queue_t localDictWriteQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

// Make this queue target the concurrent queue. Not strictly required, but you had everything executing on
// concurrentQueue before, and this preserves that, while also protecting localDict.
dispatch_set_target_queue(localDictWriteQueue, concurrentQueue);

// A dispatch group that allows us to perform an operation only after all the constituent writes have finished.
dispatch_group_t group = dispatch_group_create();

// For each platform, enter the group, then fire off the concurrent block
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
    // Fetch into a dictionary that is local to the block.
    NSDictionary* x = [self getDataWithPlatform:@"ps4"];

    // Then, on the serial localDictWriteQueue merge those entries into the shared local dictionary
    dispatch_async(localDictWriteQueue, ^{
        [localDict addEntriesFromDictionary: x];
        // This balances out the dispatch_group_enter operation we did right before we enqueued this
        dispatch_group_leave(group);
    });
});

// Second verse, same as the first
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
    NSDictionary* x = [self getDataWithPlatform:@"xboxone"];
    dispatch_async(localDictWriteQueue, ^{
        [localDict addEntriesFromDictionary: x];
        dispatch_group_leave(group);
    });
});

// Third verse, same as the first
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
    NSDictionary* x = [self getDataWithPlatform:@"pc"];
    dispatch_async(localDictWriteQueue, ^{
        [localDict addEntriesFromDictionary: x];
        dispatch_group_leave(group);
    });
});

// Then set up the block we want to run at the end... use main queue because it updates UI.
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Update self.releasesDict all at once, on the main thread
    // Note: we do not need to do this read of localDict on localDictWriteQueue because if this block is executing, we know (semantically) that all possible
    // write operations have already completed, and that no other writes to localDict are possible because it's local to this method call.
    self.releasesDict = localDict;
    // Same idea
    self.releasesArr = [self.releasesDict allKeys];

    // Update UI based on state changes to self.
    [self.tableView reloadData];
    [self.activityInd stopAnimating];
});