Unstructured Concurrency(非结构化并发)Structured Concurrency(结构化并发) 是并发编程中的两种不同的模型,它们在任务管理、生命周期和错误处理上有显著的区别。下面是它们的主要区别:

1. 任务生命周期管理

  • Unstructured Concurrency(非结构化并发):

    • 在非结构化并发模型中,任务的生命周期由开发者手动管理,任务可能在不同的时间点启动和结束。
    • 开发者需要自己确保任务的正确完成,可能导致资源泄漏、任务未完成或者未正确取消的情况。
    • 例如,在使用 DispatchQueueThread 时,任务可以启动并独立运行,直到它们被显式地取消或完成。这种方式容易导致一些未处理的任务,因为它们没有明确的上下文来管理。
  • Structured Concurrency(结构化并发):

    • 在结构化并发模型中,任务的生命周期由其父任务管理。任务会在其父任务的范围内创建,父任务负责等待子任务的完成,确保所有任务在父任务结束前完成。
    • 结构化并发通过这种明确的结构来避免任务悬空或遗漏,保证任务的完整性和正确的资源管理。
    • 例如,Swift 的 TaskGroupactor 就是结构化并发的示例。任务在 TaskGroupactor 中的作用域内被管理,父任务会等待子任务的完成。

2. 错误处理

  • Unstructured Concurrency(非结构化并发):

    • 在非结构化并发中,任务之间的错误传播和捕获可能比较混乱,开发者需要手动处理错误并确保任务间的异常不会导致不一致的状态。
    • 如果没有适当的错误处理机制,任务可能会在不预期的情况下失败,并且异常可能被遗漏。
  • Structured Concurrency(结构化并发):

    • 结构化并发通过将任务限制在父任务的作用域内来简化错误处理。父任务可以捕获并处理子任务的错误,或者通过传递错误进行处理。
    • 因为任务在明确的结构中进行管理,所以错误处理更加集中和可靠。例如,使用 TaskGroup,父任务可以等待并处理子任务的错误。

3. 资源管理

  • Unstructured Concurrency(非结构化并发):

    • 资源管理更依赖开发者手动管理。开发者需要确保资源(如内存、文件句柄、网络连接等)被正确释放,否则可能导致资源泄漏或程序崩溃。
    • 在非结构化并发中,如果一个任务没有显式取消或完成,相关的资源可能会长期占用。
  • Structured Concurrency(结构化并发):

    • 由于任务生命周期被明确管理,资源也被更好地控制。在结构化并发中,父任务会等待子任务的完成,这使得任务和资源的管理更具可控性和安全性。
    • 当任务完成时,相关的资源也会被正确释放,避免资源泄漏。

4. 并发任务的可见性

  • Unstructured Concurrency(非结构化并发):

    • 在非结构化并发中,任务的启动和执行常常是分散的,任务之间没有明确的关系,可能导致一些任务无法被跟踪。
    • 任务可能是在任何地方启动的,无法确保它们会在程序的某个特定点完成。
  • Structured Concurrency(结构化并发):

    • 在结构化并发中,任务是有父子关系的,每个任务的生命周期都明确由父任务管理。因此,任务的可见性和管理更加清晰。
    • 子任务只在父任务的作用域内有效,确保了任务的顺序和执行状态。

5. 示例

  • Unstructured Concurrency 示例(非结构化并发)
// 使用 DispatchQueue 启动异步任务
DispatchQueue.global().async {
    // 异步任务
    print("Task 1")
}

DispatchQueue.global().async {
    // 另一个异步任务
    print("Task 2")
}

在这个例子中,Task 1Task 2 被独立地启动,且没有父任务来管理它们的生命周期,它们的执行顺序和完成情况无法确保。

  • Structured Concurrency 示例(结构化并发)
import Foundation

func fetchData() async -> String {
    return "Data Loaded"
}

func performTasks() async {
    // 创建一个 TaskGroup
    await withTaskGroup(of: String.self) { group in
        group.addTask {
            await fetchData() // 异步任务 1
        }
        group.addTask {
            await fetchData() // 异步任务 2
        }

        for await result in group {
            print(result)
        }
    }
}

Task {
    await performTasks()
}

在这个例子中,所有任务都在 TaskGroup 中管理,确保它们的生命周期由父任务(performTasks)管理。所有子任务都会在父任务结束前完成,确保了任务的可追踪性和资源的合理释放。

总结

  • 非结构化并发:任务的生命周期不受父任务管理,开发者需要手动管理并发任务,容易导致资源泄漏和错误处理不当。
  • 结构化并发:任务在父任务的作用域内进行管理,确保任务有明确的生命周期,父任务负责等待所有子任务完成,提供更高的可靠性和易于管理的并发模型。

结构化并发通过简化任务管理和错误处理,提供了比非结构化并发更加可靠和可维护的并发编程模型。