আমি কীভাবে ফোরিচ সহ অ্যাসিঙ্ক ব্যবহার করতে পারি?


123

ফরইচ ব্যবহার করার সময় অ্যাসিঙ্ক ব্যবহার করা কি সম্ভব? নীচে কোডটি চেষ্টা করছি:

using (DataContext db = new DataLayer.DataContext())
{
    db.Groups.ToList().ForEach(i => async {
        await GetAdminsFromGroup(i.Gid);
    });
}

আমি ত্রুটি পাচ্ছি:

'Async' নামটি বর্তমান প্রসঙ্গে নেই

ব্যবহারের বিবৃতিটি যে পদ্ধতিতে আবদ্ধ রয়েছে তা async এ সেট করা আছে।

উত্তর:


180

List<T>.ForEachasync( বিশেষ কারণে লিনকু-টু-অবজেক্টগুলিও হয় না) দিয়ে বিশেষত ভাল খেলেন না ।

এই ক্ষেত্রে, আমি প্রতিটি উপাদানকে একটি অ্যাসিনক্রোনাস অপারেশনে প্রজেক্ট করার পরামর্শ দিচ্ছি এবং আপনি তারপরে (অযৌক্তিকভাবে) সমস্তগুলি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে পারেন।

using (DataContext db = new DataLayer.DataContext())
{
    var tasks = db.Groups.ToList().Select(i => GetAdminsFromGroupAsync(i.Gid));
    var results = await Task.WhenAll(tasks);
}

একজন asyncপ্রতিনিধিকে দেওয়ার বিষয়ে এই পদ্ধতির সুবিধাগুলি হ'ল ForEach:

  1. ত্রুটি পরিচালনায় আরও সঠিক। ব্যতিক্রমগুলি async voidধরা যায় না catch; এই পদ্ধতিটি await Task.WhenAllপ্রাকৃতিক ব্যতিক্রম হ্যান্ডলিংকে মঞ্জুরি দিয়ে লাইনে ব্যতিক্রমগুলি প্রচার করবে ।
  2. আপনি কি জানেন যে কাজগুলো এই পদ্ধতি শেষে সম্পূর্ণ হয়, যেহেতু এটি একটি করে await Task.WhenAll। আপনি যদি ব্যবহার করেন তবে async voidকখন অপারেশনগুলি সম্পন্ন হবে তা আপনি সহজেই বলতে পারবেন না।
  3. ফলাফলগুলি পুনরুদ্ধার করার জন্য এই পদ্ধতির একটি প্রাকৃতিক বাক্য গঠন রয়েছে। GetAdminsFromGroupAsyncমনে হচ্ছে এটি এমন একটি অপারেশন যা ফলাফল (প্রশাসকদের) উত্পাদন করে এবং এই জাতীয় কোডটি আরও স্বাভাবিক যদি এই ধরনের ক্রিয়াকলাপগুলি পার্শ্ব প্রতিক্রিয়া হিসাবে মান নির্ধারণের পরিবর্তে ফলাফলগুলি ফিরিয়ে দিতে পারে।

5
এমন নয় যে এটি কোনও পরিবর্তন করে, তবে List.ForEach()লিনকুইয়ের অংশ নয়।
এসভি

@ স্টেফেনক্লিয়ারি দুর্দান্ত পরামর্শ এবং আপনি যে উত্তর প্রদান করেছেন তার জন্য আপনাকে ধন্যবাদ async। তারা খুব সহায়ক হয়েছে!
জাস্টিন হেলগারসন

4
@ স্টিওয়ার্টএন্ডারসন: কাজগুলি একই সাথে সম্পাদন করবে। সিরিয়াল কার্যকর করার জন্য কোনও এক্সটেনশন নেই; শুধু একটি না foreachএকটি সঙ্গে awaitআপনার লুপ শরীরের।
স্টিফেন ক্লিয়ারি

1
@ ম্যারে: ForEachকেবলমাত্র একটি সিঙ্ক্রোনাস প্রতিনিধি প্রকার গ্রহণ করে এবং অ্যাসিনক্রোনাস প্রতিনিধি প্রকার গ্রহণের জন্য কোনও ওভারলোড নেই। সুতরাং সংক্ষিপ্ত উত্তরটি "কেউই অ্যাসিক্রোনাস লিখেনি ForEach"। দীর্ঘ উত্তরটি হ'ল আপনাকে কিছু শব্দার্থবিজ্ঞান অনুমান করতে হবে; উদাহরণস্বরূপ, আইটেমগুলি একবারে (যেমন foreach) একবারে প্রক্রিয়া করা উচিত , বা একসাথে (পছন্দ করা Select) হবে? যদি একবারে এক, অ্যাসিনক্রোনাস স্ট্রিমগুলি কি আরও ভাল সমাধান হতে পারে না? যদি একই সাথে হয়, ফলাফলগুলি মূল আইটেম ক্রমে বা সমাপ্তির ক্রমে হওয়া উচিত? এটি কি প্রথম ব্যর্থতায় ব্যর্থ হওয়া উচিত বা সমস্ত শেষ না হওয়া পর্যন্ত অপেক্ষা করা উচিত? ইত্যাদি
স্টিফেন ক্লিয়ারি

2
@ রজার ওল্ফ: হ্যাঁ; SemaphoreSlimঅ্যাসিক্রোনাস টাস্ক থ্রোটল ব্যবহার করুন ।
স্টিফেন ক্লিয়ারি

61

এই ছোট্ট এক্সটেনশন পদ্ধতিটি আপনাকে ব্যতিক্রম-নিরাপদ async পুনরাবৃত্তি দিতে হবে:

public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
{
    foreach (var value in list)
    {
        await func(value);
    }
}

আমরা থেকে ল্যামডা এর রিটার্ন টাইপ পরিবর্তন করছি যেহেতু voidথেকে Task, ব্যতিক্রম সঠিকভাবে আপ সঞ্চারিত হবে। এটি আপনাকে অনুশীলনে এই জাতীয় কিছু লেখার অনুমতি দেবে:

await db.Groups.ToList().ForEachAsync(async i => {
    await GetAdminsFromGroup(i.Gid);
});

আমি বিশ্বাস করি এর asyncআগে হওয়া উচিতi =>
টড

ফরএচএ্যাসিন () অপেক্ষা করার পরিবর্তে, কেউ অপেক্ষা করতেও পারে ()।
জোনাস

ল্যাম্বদার এখানে অপেক্ষা করার দরকার নেই।
হাজ্জিক

আমি সমর্থন CancellationToken জন্য যে মধ্যে টড এর উত্তর হিসেবে এখানে যোগ হবে stackoverflow.com/questions/29787098/...
Zorkind

ForEachAsyncমূলত একটি লাইব্রেরি পদ্ধতি, তাই অপেক্ষা সম্ভবত কনফিগার করা যায় ConfigureAwait(false)
থিওডর জৌলিয়াস

9

সহজ উত্তরটি হ'ল পদ্ধতিটির foreachপরিবর্তে কীওয়ার্ডটি ব্যবহার করা ।ForEach()List()

using (DataContext db = new DataLayer.DataContext())
{
    foreach(var i in db.Groups)
    {
        await GetAdminsFromGroup(i.Gid);
    }
}

আপনি একজন প্রতিভা
ভিক_নরইলস

8

অনুক্রমিক প্রক্রিয়াজাতকরণ সহ উপরের অ্যাসিঙ্ক ফোর্যাচ বৈকল্পিকগুলির আসল কার্যকারী সংস্করণ এখানে রয়েছে:

public static async Task ForEachAsync<T>(this List<T> enumerable, Action<T> action)
{
    foreach (var item in enumerable)
        await Task.Run(() => { action(item); }).ConfigureAwait(false);
}

বাস্তবায়ন এখানে:

public async void SequentialAsync()
{
    var list = new List<Action>();

    Action action1 = () => {
        //do stuff 1
    };

    Action action2 = () => {
        //do stuff 2
    };

    list.Add(action1);
    list.Add(action2);

    await list.ForEachAsync();
}

মূল পার্থক্য কি? .ConfigureAwait(false);যা প্রতিটি টাস্কের অ্যাসিঙ্ক ক্রমিক প্রক্রিয়া করার সময় মূল থ্রেডের প্রসঙ্গ রাখে।


6

দিয়ে শুরু করে C# 8.0, আপনি অবিচ্ছিন্নভাবে স্ট্রিম তৈরি এবং গ্রাস করতে পারেন।

    private async void button1_Click(object sender, EventArgs e)
    {
        IAsyncEnumerable<int> enumerable = GenerateSequence();

        await foreach (var i in enumerable)
        {
            Debug.WriteLine(i);
        }
    }

    public static async IAsyncEnumerable<int> GenerateSequence()
    {
        for (int i = 0; i < 20; i++)
        {
            await Task.Delay(100);
            yield return i;
        }
    }

অধিক


1
এটির সুবিধাটি রয়েছে যে প্রতিটি উপাদানটির অপেক্ষার শীর্ষে, আপনি এখন MoveNextগণকের সন্ধানের অপেক্ষায় রয়েছেন। এটি সেই ক্ষেত্রে গুরুত্বপূর্ণ যেখানে গণক পরবর্তী তাত্ক্ষণিক তাত্ক্ষণিকভাবে আনতে পারে না এবং এটি উপলব্ধ হওয়ার জন্য অবশ্যই অপেক্ষা করতে হবে।
থিওডর জৌলিয়াস

3

এই এক্সটেনশন পদ্ধতিটি যুক্ত করুন

public static class ForEachAsyncExtension
{
    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
    {
        return Task.WhenAll(from partition in Partitioner.Create(source).GetPartitions(dop) 
            select Task.Run(async delegate
            {
                using (partition)
                    while (partition.MoveNext())
                        await body(partition.Current).ConfigureAwait(false);
            }));
    }
}

এবং তারপরে যেমন ব্যবহার করুন:

Task.Run(async () =>
{
    var s3 = new AmazonS3Client(Config.Instance.Aws.Credentials, Config.Instance.Aws.RegionEndpoint);
    var buckets = await s3.ListBucketsAsync();

    foreach (var s3Bucket in buckets.Buckets)
    {
        if (s3Bucket.BucketName.StartsWith("mybucket-"))
        {
            log.Information("Bucket => {BucketName}", s3Bucket.BucketName);

            ListObjectsResponse objects;
            try
            {
                objects = await s3.ListObjectsAsync(s3Bucket.BucketName);
            }
            catch
            {
                log.Error("Error getting objects. Bucket => {BucketName}", s3Bucket.BucketName);
                continue;
            }

            // ForEachAsync (4 is how many tasks you want to run in parallel)
            await objects.S3Objects.ForEachAsync(4, async s3Object =>
            {
                try
                {
                    log.Information("Bucket => {BucketName} => {Key}", s3Bucket.BucketName, s3Object.Key);
                    await s3.DeleteObjectAsync(s3Bucket.BucketName, s3Object.Key);
                }
                catch
                {
                    log.Error("Error deleting bucket {BucketName} object {Key}", s3Bucket.BucketName, s3Object.Key);
                }
            });

            try
            {
                await s3.DeleteBucketAsync(s3Bucket.BucketName);
            }
            catch
            {
                log.Error("Error deleting bucket {BucketName}", s3Bucket.BucketName);
            }
        }
    }
}).Wait();

2

সমস্যাটি ছিল asyncকীওয়ার্ডটি লাম্বদার সামনে উপস্থিত হওয়া দরকার, দেহের আগে নয়:

db.Groups.ToList().ForEach(async (i) => {
    await GetAdminsFromGroup(i.Gid);
});

35
অপ্রয়োজনীয় এবং সূক্ষ্ম ব্যবহারের জন্য -1 async void। এই পদ্ধতির ব্যতিক্রম হ্যান্ডেল এবং অ্যাসিক্রোনাস অপারেশনগুলি কখন সম্পন্ন হবে তা জেনে সমস্যা রয়েছে।
স্টিফেন ক্লিয়ারি

হ্যাঁ, আমি পেয়েছি এটি ব্যতিক্রমগুলি সঠিকভাবে পরিচালনা করে না।
হারমান শোএনফিল্ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.