تشغيل خيوط متعددة ، بدء تشغيل جديد مع انتهاء آخر

StackOverflow https://stackoverflow.com/questions/2196945

  •  25-09-2019
  •  | 
  •  

سؤال

لدي تطبيق له العديد من الحالات. كل حالة لديها العديد من ملفات TIF متعددة. أحتاج إلى سرية ملفات TF إلى ملف PDF. نظرًا لوجود العديد من الملفات ، اعتقدت أنه يمكنني ربط عملية التحويل. أقوم حاليًا بتقييد العملية على عشرة تحويلات في وقت واحد (أي عشرة أدوات). عند اكتمال تحويل واحد ، يجب أن يبدأ آخر.

هذا هو الإعداد الحالي الذي أستخدمه.

private void ConvertFiles()
{
  List<AutoResetEvent> semaphores = new List<AutoResetEvet>();
  foreach(String fileName in filesToConvert)
  {
    String file = fileName;

    if(semaphores.Count >= 10)
    {
      WaitHandle.WaitAny(semaphores.ToArray());
    }


    AutoResetEvent semaphore = new AutoResetEvent(false);
    semaphores.Add(semaphore);

    ThreadPool.QueueUserWorkItem(
      delegate
      { 
        Convert(file);
        semaphore.Set();
        semaphores.Remove(semaphore);
      }, null);
  }

  if(semaphores.Count > 0)
  {
    WaitHandle.WaitAll(semaphores.ToArray());
  }
}

باستخدام هذا ، يؤدي في بعض الأحيان إلى استثناء يوضح أن معلمات صفيف waithandle.waitall () أو waithandle.waitany () لا تتجاوز طولها 65. ما الخطأ الذي أفعله في هذا النهج وكيف يمكنني تصحيحه؟

هل كانت مفيدة؟

المحلول

هناك بعض المشاكل مع ما كتبته.

1 ، إنه ليس مؤلمًا. لديك عدة مؤشرات ترابط إضافة وإزالة وانتظار مجموعة من التلقائيات التلقائية. يمكن الوصول إلى العناصر الفردية للقائمة على مؤشرات ترابط منفصلة ، ولكن أي شيء يضيف أو يزيل أو يفحص جميع العناصر (مثل مكالمة Waitany) ، تحتاج إلى القيام بذلك داخل القفل.

2nd ، ليس هناك ما يضمن أن الكود الخاص بك سوف يعالج 10 ملفات فقط في وقت واحد. الكود بين عندما يتم فحص حجم القائمة ، والنقطة التي يتم فيها إضافة عنصر جديد مفتوح لارتفاع مؤشرات ترابط للوصول.

الثالث ، هناك احتمال أن تبدأ مؤشرات الترابط في قائمة الانتظار في قائمة الانتظار لتحويل نفس الملف. دون التقاط اسم الملف داخل الحلقة ، سيستخدم مؤشر الترابط الذي يحول الملف أي قيمة في اسم الملف عند تنفيذها ، وليس كل ما كان في اسم الملف عند استدعاء QueueUserWorkItem.

يجب أن توجهك مقالة codeproject هذه في الاتجاه الصحيح لما تحاول القيام به: http://www.codeproject.com/kb/threads/schedulingengine.aspx

تعديل:

var semaphores = new List<AutoResetEvent>();
        foreach (String fileName in filesToConvert)
        {
            String file = fileName;
            AutoResetEvent[] array;
            lock (semaphores)
            {
                array = semaphores.ToArray();
            }
            if (array.Count() >= 10)
            {
                WaitHandle.WaitAny(array);
            }

            var semaphore = new AutoResetEvent(false);
            lock (semaphores)
            {
                semaphores.Add(semaphore);
            }
            ThreadPool.QueueUserWorkItem(
              delegate
              {
                  Convert(file);
                  lock (semaphores)
                  {
                      semaphores.Remove(semaphore);
                  }
                  semaphore.Set();
              }, null);
        }

أنا شخصياً لا أعتقد أنني سأفعل ذلك بهذه الطريقة ... لكن العمل مع الكود الذي لديك ، يجب أن ينجح هذا.

نصائح أخرى

هل تستخدم إشارة حقيقية (System.Threading))؟ عند استخدام Semaphores ، يمكنك عادةً تخصيص موارد Max الخاصة بك وسيحظرك تلقائيًا (أثناء إضافة وإصدار). يمكنك الذهاب مع نهج Waitany ، لكنني أشعر بأنك اخترت الطريق الأكثر صعوبة.

يبدو أنك بحاجة إلى إزالة المقبض الذي تم تشغيل وظيفة Waitany للمتابعة

if(semaphores.Count >= 10)
{
  int index = WaitHandle.WaitAny(semaphores.ToArray());
  semaphores.RemoveAt(index);
}

لذلك في الأساس أود إزالة:

semaphores.Remove(semaphore);

اتصل من الخيط واستخدم ما سبق لإزالة الحدث المشير ومعرفة ما إذا كان ذلك يعمل.

ربما لا يجب أن تنشئ الكثير من الأحداث؟

// input
var filesToConvert = new List<string>();
Action<string> Convert = Console.WriteLine;

// limit
const int MaxThreadsCount = 10;

var fileConverted = new AutoResetEvent(false);
long threadsCount = 0;

// start
foreach (var file in filesToConvert) {
    if (threadsCount++ > MaxThreadsCount) // reached max threads count 
        fileConverted.WaitOne();          // wait for one of started threads

    Interlocked.Increment(ref threadsCount);

    ThreadPool.QueueUserWorkItem(
        delegate {
            Convert(file);

            Interlocked.Decrement(ref threadsCount);
            fileConverted.Set();
        });
}

// wait
while (Interlocked.Read(ref threadsCount) > 0) // paranoia?
    fileConverted.WaitOne();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top