调度组和线程快速处理(异步等待)

时间:2020-09-22 12:58:15

标签: swift xcode multithreading grand-central-dispatch

所以我正在练习线程,但我有一个要求。 我想等待第一个api调用完成,然后执行第二个api(就像等待异步),如果第一个未完成,我想在那里停止代码,直到完成。

所以,我写了这个。告诉我是否正确,或者是否有更好的方法

func myFunction() {
    var a: Int?
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
        a = 1
        print("First")
        group.leave()
    }
    group.wait()
    group.enter()
    DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
        a = 3
        print("Second")
        group.leave()
    }
    // wait ...
    group.wait()
    group.notify(queue: .main) {
        print(a) // y
    }
}

3 个答案:

答案 0 :(得分:0)

别等。

在第一个的完成处理程序中运行第二个API调用

var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');


module.exports = {
    entry: { index: path.resolve(__dirname, "static_src", "index.js") },
    output: {
    path: path.resolve(__dirname, "static_compiled"),
    publicPath: "/static/", // Should match Django STATIC_URL
    filename: "[name].js",  // No filename hashing, Django takes care of this
    chunkFilename: "[id]-[chunkhash].js", // DO have Webpack hash chunk filename, see below
    },
    plugins: [
    new BundleTracker({filename: './webpack-stats.json'}),
    ],
    module: {
    rules: [
        {
        test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader'}, // to transform JSX into JS
        {
        test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"]},
        {
        test: /\.css$/, use: ["style-loader", "css-loader"]},
        {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        loader: 'file-loader'},
        {
        test: /\.svg$/,
        loader: 'svg-inline-loader'},
        /* {
           test: /\.(ico|png|jpg|gif)$/,
           loader: 'file-loader'} */
        {
        test: /\.(ico|png|svg|jpg|gif|jpe?g)$/,
        use: [
            {
            options: {
                name: "[name].[ext]",
                outputPath: "img/"
            },
            loader: "file-loader"
            }
        ]
        }

        
    ],
    },
    resolve: {
    alias: {
            shared: path.resolve(__dirname, 'node_modules', 'bower_components')
    },
    extensions: ['.js', '.ts', 'jsx', 'tsx']
    },
    devServer: {
    writeToDisk: true, // Write files to disk in dev mode, so Django can serve the assets
    }
}

答案 1 :(得分:0)

就像等待异步

对于Swift来说,缺乏内置的语言级别await async机制是一个严重的问题,并且可能会在以后的版本中得到解决。同时,Swift基本上没有内置的语言级线程执行机制。您必须通过与Cocoa / Objective-C(大型中央调度,NSOperation)或其他处理异步性的框架(Combine)进行对话来手动处理。

您关注的特定问题是我所说的串行化异步性。有几种方法可以做到这一点。

大中央派遣

最简单的方法是,如您所知,在第一个异步调用的执行处理程序的末尾执行第二个异步调用。显然,这对于安排许多任务不是很普遍。

您当然可以按照概述的方式使用DispatchGroup。您对模式的陈述几乎是正确的。您不需要最后一个wait,而且更重要的是,您必须从头开始进入后台队列,因为在主队列上使用DispatchGroup是非法的。就目前而言,您的代码可以有效地阻止我们等待的主队列,这是您绝对不能做的事情。

操作队列

您可以创建一个串行OperationQueue并将Operation对象加载到其上。这是一个更为通用的机制。您可以使Operation对象彼此依赖。在Combine框架问世之前,这是最佳的通用解决方案。

组合框架

如果您可以将自己限制在iOS 13及更高版本中,则Combine框架是最常规的异步序列化方法。有关进一步的讨论,请参见Combine framework serialize async operations

答案 2 :(得分:0)

Swift 5.5 和 Xcode 13.0 带来了 async/await,因此您可以执行以下操作:


import Foundation

print("Hello, World!")

// Returns a 1 after a 3-second delay
func giveMeAOne() async -> Int {
    Thread.sleep(forTimeInterval: 3.0)
    return 1
}

// Returns a 5 after a 3-second delay
func giveMeAFive() async -> Int {
    Thread.sleep(forTimeInterval: 3.0)
    return 5
}

func myFunction() async {
    var a: Int = 0
    print(a)
    a += await giveMeAOne()
    print(a)
    a += await giveMeAFive()
    print(a)
}

async {
    await myFunction()
}

sleep(7) // added at the end so that process doesn't exit right away

请注意,上述内容是在 Xcode 中作为 macOS 命令行工具构建的,而不是使用 Swift Playgrounds。从 Xcode 13.0 beta 1 开始,不幸的是,Swift Playgrounds 在很多的异步/等待内容上失败了。

上面的代码按顺序运行它们,并对 myFunction 进行了相当多的清理。但是请注意,这不会引入任何并发giveMeAOne()giveMeAFive() 都按顺序运行。

如果您想同时运行 4 个函数调用,您可以这样做:

/// Returns a random integer from 1 through 10, after a 6-second delay
func randomSmallInt() async -> Int {
    Thread.sleep(forTimeInterval: 6)
    return Int.random(in: 1 ... 10)
}

/// Fetches 4 random integers, summing them
func myFunction() async {
    // print timestamp
    print(Date())
    async let a = randomSmallInt()
    async let b = randomSmallInt()
    async let c = randomSmallInt()
    async let d = randomSmallInt()
    
    // This awaits for a, b, c, and d to all complete.
    // You can only await a value once.
    let numbersToAdd = await [a, b, c, d]
    var sum = 0
    for number in numbersToAdd {
        print("adding \(number)")
        sum += number
    }
    print("Sum = \(sum)")
    
    // print another timestamp, so you can see that the async let statements ran
    // concurrently, rather than sequentially
    print(Date())
}

async {
    await myFunction()
}

// make sure we have time to finish before process exits
Thread.sleep(forTimeInterval: 10)

当你运行这个时,你可以从时间戳看到总执行时间只花了大约 6 秒——因为所有的 async let ... 语句都是并发执行的。

如果您有固定数量的需要异步调用的东西,这很有效。但是,如果您想进行 100 次异步函数调用,比如加载图像或其他东西,该怎么办?

你可能认为你可以做这样的事情,这会奏效,但会同步运行所有东西——可能不是你想要的:

// Don't do this -- these all run sequentially
var sum = 0
for _ in 0 ..< 100 {
    async let value = randomSmallishInt()
    let number = await value
    print("Adding \(number)")
    sum += number
}

如果您想做任意未知数量的异步事情,最好使用taskGroup。为了使事情更清晰,这段代码比它需要的要冗长一些。 注意:Xcode 13.0 beta 在 print 语句输出方面存在问题,缺少大部分。无论如何,这里是:

/// Returns a random integer from 1 through 10
func randomSmallInt() async -> Int {
    Thread.sleep(forTimeInterval: 6)
    return Int.random(in: 1 ... 10)
}

func myFunction_arbitrary_repetitions_asynchronously() async {
    // print timestamp
    print(Date())
    
    // This is pretty cool -- 
    // `withTaskGroup` creates a context for executing a group of async tasks
    // In this case, `Int.self` specifies that each individual task will return
    // an Int.  `[Int].self` specifies that the entire group will return an array of Ints:
    let numbers = await withTaskGroup(of: Int.self, returning: [Int].self) { group in
        // Repeat 100 times
        for _ in 0 ..< 100 {
            // Run the next thing asynchronously
            group.async {
                return await randomSmallInt()
            }
        }
        
        // Iterate through results
        var result: [Int] = []
        for await individualResult in group {
            result.append(individualResult)
        }
        return result
    }
    
    var sum = 0
    for number in numbers {
        print("Adding \(number)")
        sum += number
    }
    print("Sum = \(sum)")
    // print another timestamp, so you can see that the async let statements ran
    // concurrently, rather than sequentially
    print(Date())
}

async { await myFunction_arbitrary_repetitions_asynchronously() }
Thread.sleep(10)

你可以大大缩短这个时间:

func myFunction_arbitrary_repetitions_asynchronously_short() async {
    // print timestamp
    print(Date())
    
    // In this case, the taskGroup just returns the sum
    let sum = await withTaskGroup(of: Int.self, returning: Int.self) { group in
        // Repeat 100 times
        for _ in 0 ..< 100 {
            // Run the next thing asynchronously
            group.async {
                return await randomSmallInt()
            }
        }
        
        // Iterate through results
        var sum = 0
        for await individualResult in group {
            print("Adding \(individualResult)")
            sum += individualResult
        }
        return sum
    }

    print("Sum = \(sum)")
    // print another timestamp, so you can see that the async let statements ran
    // concurrently, rather than sequentially
    print(Date())
}
async { await myFunction_arbitrary_repetitions_asynchronously_short() }

重要提示:Xcode 13.0 beta 1 在处理此代码时变得不稳定。具体来说,调试器喜欢在进程尚未完成时分离。例如,许多 print 语句的输出不会出现,并且在异步代码中使用断点很困难。构建命令行工具,然后运行生成的可执行文件(在 ~/Library/Developer/Xcode/DerivedData//Build/Products/Debug 中找到)至少会显示打印语句的所有输出.