طريقة غير متزامنة مع foreach

لدي بعض طريقة المتزامن

 public static Task GetUser(NameValueCollection parameters)
        {
            return CallMethodApi("users.get", parameters, CallType.HTTPS);
        }

وأنا أكتب الطريقة أدناه

public static IEnumerable GetUsers(IEnumerable usersUids, Field fields)
{
    foreach(string uid in usersUids)
    {
        var parameters = new NameValueCollection
                             {
                                 {"uids", uid},
                                 {"fields", FieldsUtils.ConvertFieldsToString(fields)}
                             };
        yield return GetUser(parameters).Result;
    }
}

هذه الطريقة غير متزامنة؟ كيف تكتب هذا باستخدام Parallel.ForEach؟

0

2 إجابة

شيء من هذا القبيل.

public static IEnumerable GetUsers(IEnumerable usersUids, Field fields)
{
    var results = new List
    Parallel.ForEach(usersUids, uid => {
        var parameters = new NameValueCollection
                             {
                                 {"uids", uid},
                                 {"fields", FieldsUtils.ConvertFieldsToString(fields)}
                             };
        var user = GetUser(parameters).Result;
        lock(results)
            results.Add(user);
    });
    return results;
}

ملاحظة: لن تكون النتائج بنفس الترتيب المتوقع.

0
وأضاف
شكرا bojan ... أنا فقط اكتب رمز في مع عدم وجود اختبار لاستخدامها كدليل توضيحي ... يبدو أنها قد استخدمت للتو حرفيا. أوتش! أصلحت حالة السباق على أي حال
وأضاف المؤلف Orion Edwards, مصدر
شكر! عمل جيد.
وأضاف المؤلف BILL, مصدر
تجدر الإشارة إلى وجود شرط سباق في الشفرة أعلاه ، حيث يتم تعديل قائمة النتائج بشكل متزامن. النتائج العشوائية والفشل سيحدث بشكل عشوائي. على الأقل ستحتاج إلى قفل حول العملية Add .
وأضاف المؤلف Bojan Resnik, مصدر

طريقتك ليست متزامنة. بافتراض أن طريقة GetUser تبدأ بالفعل مهمة غير متزامنة ، Parallel.ForEach ستستخدم سلاسل إضافية فقط لبدء مهامك ، وهو على الأرجح ليس ما انت تريد.

بدلاً من ذلك ، ما تريد القيام به على الأرجح هو بدء جميع المهام والانتظار حتى تنتهي:

public static IEnumerable GetUsers(IEnumerable usersUids, Field fields)
{
    var tasks = usersUids.Select(
        uid =>
        {
            var parameters = new NameValueCollection
            {
                {"uids", uid},
                {"fields", FieldsUtils.ConvertFieldsToString(fields)}
            };
            return GetUser(parameters);
        }
    ).ToArray();

    Task.WaitAll(tasks);

    var result = new JObject[tasks.Length];
    for (var i = 0; i < tasks.Length; ++i)
        result[i] = tasks[i].Result;

    return result;
}

إذا كنت ترغب أيضًا في بدء تشغيلها بالتوازي ، فيمكنك استخدام PLINQ:

    var tasks = usersUids.AsParallel().AsOrdered().Select(
        uid =>
        {
            var parameters = new NameValueCollection
            {
                {"uids", uid},
                {"fields", FieldsUtils.ConvertFieldsToString(fields)}
            };
            return GetUser(parameters);
        }
    ).ToArray();

يحافظ كل من مقتطف الشفرة على الترتيب النسبي للأحمال والكائنات التي تم إرجاعها - result [0] يتوافق مع usersUids [0] ، وما إلى ذلك.

0
وأضاف