.شبكة:كيفية الحصول على بيانات الموضوع الرئيسي لإشارة الخيط الخلفية متاحة؟

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

سؤال

ما هي التقنية المناسبة لديك الموضوع أ الإشارة الموضوع ب من بعض الأحداث، دون أن يكون الموضوع ب الجلوس محجوبًا في انتظار حدوث حدث ما؟

لدي موضوع في الخلفية سيقوم بملء القائمة المشتركة<T>.أحاول إيجاد طريقة للإشارة بشكل غير متزامن إلى الخيط "الرئيسي" بأن هناك بيانات متاحة ليتم التقاطها.


لقد فكرت في تعيين حدث باستخدام كائن EventWaitHandle، لكن لا يمكنني جعل مؤشر الترابط الرئيسي الخاص بي موجودًا في Event.WaitOne().


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


لقد اعتبرت أن لدي رد اتصال مندوب يبدأ ببساطة فاصل زمني صفري System.Windows.Forms.Timer (مع مزامنة الوصول إلى مؤشر الترابط إلى المؤقت).بهذه الطريقة يجب أن يكون الخيط عالقًا عند استدعائه

Timer.Enabled = true;

ولكن هذا يبدو وكأنه الاختراق.

في الأيام الخوالي، كان كائني ينشئ نافذة مخفية ويجعل الخيط ينشر رسائل إلى HWND لتلك النوافذ المخفية.لقد فكرت في إنشاء عنصر تحكم مخفي، لكنني أجمع أنه لا يمكنك استدعاء عنصر تحكم بدون إنشاء مقبض.بالإضافة إلى ذلك، ليس لدي واجهة مستخدم:كان من الممكن إنشاء كائني على خادم ويب أو خدمة أو وحدة تحكم، ولا أريد أن يظهر عنصر تحكم رسومي - ولا أريد تجميع التبعية على System.Windows.Forms.


لقد فكرت في جعل الكائن الخاص بي يعرض واجهة ISynchronizeInvoc، ولكن بعد ذلك سأحتاج إلى تنفيذ .Invoce()، وهذه هي مشكلتي.


ما هي التقنية المناسبة لإشارة الخيط "أ" إلى الخيط "ب" لبعض الأحداث، دون أن يظل الخيط "ب" محجوبًا في انتظار وقوع حدث ما؟

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

المحلول

فيما يلي نموذج التعليمات البرمجية لفئة System.ComponentModel.BackgroundWorker.

    private static BackgroundWorker worker = new BackgroundWorker();
    static void Main(string[] args)
    {
        worker.DoWork += worker_DoWork;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.WorkerReportsProgress = true;

        Console.WriteLine("Starting application.");
        worker.RunWorkerAsync();

        Console.ReadKey();
    }

    static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Console.WriteLine("Progress.");
    }

    static void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Starting doing some work now.");

        for (int i = 0; i < 5; i++)
        {
            Thread.Sleep(1000);
            worker.ReportProgress(i);
        }
    }

    static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("Done now.");
    }

نصائح أخرى

أقوم بدمج بعض الردود هنا.

يستخدم الوضع المثالي علامة آمنة لمؤشر الترابط مثل AutoResetEvent.ليس عليك الحظر إلى أجل غير مسمى عند الاتصال WaitOne(), ، في الواقع يحتوي على حمل زائد يسمح لك بتحديد مهلة.يعود هذا الحمل الزائد false إذا لم يتم تعيين العلم خلال الفترة الفاصلة.

أ Queue هي بنية أكثر مثالية لعلاقة المنتج/المستهلك، ولكن يمكنك تقليدها إذا كانت متطلباتك تجبرك على استخدام List.يتمثل الاختلاف الرئيسي في أنه سيتعين عليك التأكد من أن المستهلك الخاص بك يقفل الوصول إلى المجموعة أثناء استخراج العناصر؛الشيء الأكثر أمانًا هو استخدام CopyTo طريقة لنسخ جميع العناصر إلى مصفوفة، ثم حرر القفل.بالطبع، تأكد من أن المنتج الخاص بك لن يحاول تحديث ملف List بينما يتم عقد القفل.

فيما يلي تطبيق بسيط لوحدة التحكم C# يوضح كيفية تنفيذ ذلك.إذا تلاعبت بالفترات الزمنية، فقد تتسبب في حدوث أشياء مختلفة؛في هذا التكوين المحدد كنت أحاول أن أجعل المنتج يقوم بإنشاء عناصر متعددة قبل أن يتحقق المستهلك من العناصر.

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        private static object LockObject = new Object();

        private static AutoResetEvent _flag;
        private static Queue<int> _list;

        static void Main(string[] args)
        {
            _list = new Queue<int>();
            _flag = new AutoResetEvent(false);

            ThreadPool.QueueUserWorkItem(ProducerThread);

            int itemCount = 0;

            while (itemCount < 10)
            {
                if (_flag.WaitOne(0))
                {
                    // there was an item
                    lock (LockObject)
                    {
                        Console.WriteLine("Items in queue:");
                        while (_list.Count > 0)
                        {
                            Console.WriteLine("Found item {0}.", _list.Dequeue());
                            itemCount++;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("No items in queue.");
                    Thread.Sleep(125);
                }
            }
        }

        private static void ProducerThread(object state)
        {
            Random rng = new Random();

            Thread.Sleep(250);

            for (int i = 0; i < 10; i++)
            {
                lock (LockObject)
                {
                    _list.Enqueue(rng.Next(0, 100));
                    _flag.Set();
                    Thread.Sleep(rng.Next(0, 250));
                }
            }
        }
    }
}

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

إذا كنت تستخدم عامل الخلفية لبدء مؤشر الترابط الثاني واستخدم الحدث ProgressChanged لإعلام مؤشر الترابط الآخر بأن البيانات جاهزة.أحداث أخرى متاحة كذلك. يجب أن تساعدك مقالة MSDN هذه على البدء.

هناك العديد من الطرق للقيام بذلك، اعتمادًا على ما تريد القيام به بالضبط.أ طابور المنتج/المستهلك ربما هو ما تريد.للحصول على نظرة متعمقة ممتازة في المواضيع، راجع الفصل الخاص خيوط (متوفر على الإنترنت) من الكتاب الممتاز C # 3.0 باختصار.

يمكنك استخدام AutoResetEvent (أو ManualResetEvent).إذا كنت تستخدم AutoResetEvent.WaitOne(0, false)، فلن يتم حظره.على سبيل المثال:

AutoResetEvent ev = new AutoResetEvent(false);
...
if(ev.WaitOne(0, false)) {
  // event happened
}
else {
 // do other stuff
}

فئة الخلفية Worker هي الحل في هذه الحالة.إنه بناء الترابط الوحيد القادر على إرسال الرسائل بشكل غير متزامن إلى خيط الذي أنشأ كائنBackgroundWorker.داخليا BackgroundWorker يستخدم AsyncOperation فئة عن طريق استدعاء asyncOperation.Post() طريقة.

this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.asyncOperation.Post(delegateMethod, arg);

تستخدم بعض الفئات الأخرى في إطار عمل .NET أيضًا AsyncOperation:

  • تطبيقBackgroundWorker
  • SoundPlayer.LoadAsync()
  • سمتبكلينت.سينداسينك ()
  • بينج.سينداسينك ()
  • WebClient.DownloadDataAsync()
  • WebClient.DownloadFile()
  • WebClient.DownloadFileAsync()
  • العميل على شبكة الإنترنت...
  • PictureBox.LoadAsync()

إذا كان مؤشر الترابط "الرئيسي" الخاص بك هو مؤشر ترابط مضخة رسائل Windows (GUI)، فيمكنك الاستقصاء باستخدام Forms.Timer - قم بضبط الفاصل الزمني للمؤقت وفقًا لمدى السرعة التي تحتاجها لجعل مؤشر ترابط واجهة المستخدم الرسومية الخاص بك "يلاحظ" البيانات من مؤشر الترابط العامل .

تذكر لمزامنة الوصول إلى المشتركة List<> إذا كنت ستستخدم foreach, ، لتجنب CollectionModified استثناءات.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top