从并发线程等待事件的正确方法

时间:2021-10-22 11:48:14

I've got an occasional crash that has to do with the improperly finished task on a concurrent thread while an app is transitioning to background.

当应用程序转换到后台时,我偶尔会遇到与并发线程上的不正确完成任务有关的崩溃。

So I have 3 threads:

所以我有3个帖子:

  1. A (main).
  2. A(主要)。
  3. B (managed by GCD).
  4. B(由GCD管理)。
  5. C (manually created to process intensive socket operations).
  6. C(手动创建以处理密集的套接字操作)。

The scenario is the following:

方案如下:

In the applicationDidEnterBackground: handler (which is certainly executed on thread A) a long-running task is begun on thread B to complete all ongoing operations (save an application state, close a socket, etc.). In this task I need to wait until a socket properly finishes its work on thread C and only after that to continue with this long-running task.

在applicationDidEnterBackground:handler(当然在线程A上执行)中,在线程B上开始长时间运行的任务以完成所有正在进行的操作(保存应用程序状态,关闭套接字等)。在这个任务中,我需要等到套接字正确地完成它在线程C上的工作,然后才继续执行这个长时间运行的任务。

Below is simplified code:

以下是简化代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Some synchronous task.
    [stateManager saveState];

    // Here I need to wait until the socket finishes its task.
    ...

    // Continuing of the long-running task.
    ...
}

What is the acceptable way to accomplish this task. Is it OK if I do something like this?

完成此任务的可接受方式是什么。如果我做这样的事情可以吗?

while (socket.isConnected)
{
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

// Continuing of the long-running task.

Or maybe something wrong in my current architecture and I need to use NSOperation to serialize asynchronous tasks somehow for example?

或者我当前的架构可能有问题,我需要使用NSOperation以某种方式序列化异步任务?

update: The problem has been solved by using dispatch_semaphore APIs as @Rob Napier suggested.

更新:问题已通过使用@Rob Napier建议的dispatch_semaphore API解决。

2 个解决方案

#1


1  

First, do not think of these as threads, and if you're creating a thread with NSThread or performSelectorInBackground: (or even worse, pthreads), don't do that. Use GCD. GCD creates queues. Queues order blocks, which eventually do run on threads, but the threads are an implementation detail and there is not a 1:1 mapping of queues to threads. See Migrating Away from Threads for more discussion on that.

首先,不要将它们视为线程,如果您使用NSThread或performSelectorInBackground创建线程:(或者更糟糕的是,pthreads),请不要这样做。使用GCD。 GCD创建队列。队列顺序块,最终在线程上运行,但线程是实现细节,并且没有队列到线程的1:1映射。有关详细信息,请参阅迁移线程。

To the question of how to wait for some other operation, the tool you probably want is a dispatch_semaphore. You create the semaphore and hand it to both operations. Then you call dispatch_semaphore_wait when you want to wait for something, and dispatch_sempahore_signal when you want to indicate that that something has happened. See Using Dispatch Semaphores to Regulate the Use of Finite Resources for more example code. The "finite resource" in this case is "the socket." You want to wait until the other part is done using it and returned it to the pool.

对于如何等待其他操作的问题,您可能需要的工具是dispatch_semaphore。您创建信号量并将其交给两个操作。然后在想要等待某事时调用dispatch_semaphore_wait,并在想要表明发生了某些事情时调用dispatch_sempahore_signal。有关更多示例代码,请参阅使用Dispatch信号量来调节有限资源的使用。在这种情况下,“有限资源”是“套接字”。您希望等到其他部分使用它并将其返回到池中。

Semaphores will work even if you are using manual threading, but I can't emphasize enough that you should not be doing manual threading. All your concurrency should be managed through GCD. This is an important tool to overall concurrency management in iOS.

即使您使用手动线程,信号量也会起作用,但我不能强调您不应该进行手动线程化。所有并发都应该通过GCD进行管理。这是iOS中整体并发管理的重要工具。

#2


1  

I would use NSOperation with dependencies. So, you have tasks A - main thread - aka 'entry point' B - heavy boy to run in background C - something else heavy to run after socket finished

我会使用NSOperation与依赖。所以,你有任务A - 主线程 - 又名'入口点'B - 在背景C中运行的重型男孩 - 在套接字完成后运行的其他东西很重

  1. Your heavy task from "B" is OperationB
  2. “B”的繁重任务是OperationB
  3. Assume your socket framework capable of running syncronous in current thread? - then this is your OperationSend
  4. 假设您的套接字框架能够在当前线程中运行同步? - 那么这是你的OperationSend
  5. Do the rest to do in background - OperationC
  6. 在后台做其余的事 - OperationC

there you have a chain of operations, dependent on each other:

你有一系列的操作,彼此依赖:

OperationB -> OperationSend -> OperationC

OperationB - > OperationSend - > OperationC

#1


1  

First, do not think of these as threads, and if you're creating a thread with NSThread or performSelectorInBackground: (or even worse, pthreads), don't do that. Use GCD. GCD creates queues. Queues order blocks, which eventually do run on threads, but the threads are an implementation detail and there is not a 1:1 mapping of queues to threads. See Migrating Away from Threads for more discussion on that.

首先,不要将它们视为线程,如果您使用NSThread或performSelectorInBackground创建线程:(或者更糟糕的是,pthreads),请不要这样做。使用GCD。 GCD创建队列。队列顺序块,最终在线程上运行,但线程是实现细节,并且没有队列到线程的1:1映射。有关详细信息,请参阅迁移线程。

To the question of how to wait for some other operation, the tool you probably want is a dispatch_semaphore. You create the semaphore and hand it to both operations. Then you call dispatch_semaphore_wait when you want to wait for something, and dispatch_sempahore_signal when you want to indicate that that something has happened. See Using Dispatch Semaphores to Regulate the Use of Finite Resources for more example code. The "finite resource" in this case is "the socket." You want to wait until the other part is done using it and returned it to the pool.

对于如何等待其他操作的问题,您可能需要的工具是dispatch_semaphore。您创建信号量并将其交给两个操作。然后在想要等待某事时调用dispatch_semaphore_wait,并在想要表明发生了某些事情时调用dispatch_sempahore_signal。有关更多示例代码,请参阅使用Dispatch信号量来调节有限资源的使用。在这种情况下,“有限资源”是“套接字”。您希望等到其他部分使用它并将其返回到池中。

Semaphores will work even if you are using manual threading, but I can't emphasize enough that you should not be doing manual threading. All your concurrency should be managed through GCD. This is an important tool to overall concurrency management in iOS.

即使您使用手动线程,信号量也会起作用,但我不能强调您不应该进行手动线程化。所有并发都应该通过GCD进行管理。这是iOS中整体并发管理的重要工具。

#2


1  

I would use NSOperation with dependencies. So, you have tasks A - main thread - aka 'entry point' B - heavy boy to run in background C - something else heavy to run after socket finished

我会使用NSOperation与依赖。所以,你有任务A - 主线程 - 又名'入口点'B - 在背景C中运行的重型男孩 - 在套接字完成后运行的其他东西很重

  1. Your heavy task from "B" is OperationB
  2. “B”的繁重任务是OperationB
  3. Assume your socket framework capable of running syncronous in current thread? - then this is your OperationSend
  4. 假设您的套接字框架能够在当前线程中运行同步? - 那么这是你的OperationSend
  5. Do the rest to do in background - OperationC
  6. 在后台做其余的事 - OperationC

there you have a chain of operations, dependent on each other:

你有一系列的操作,彼此依赖:

OperationB -> OperationSend -> OperationC

OperationB - > OperationSend - > OperationC