سؤال

أواجه مشكلة عندما أنتظر مهمة بعد أن قمت بإلغائها باستخدام CancellationTokenSource.لا تؤدي مكالمة الإلغاء إلى مقاطعة المهمة.عندما أنتظر المهمة ، تقوم كتل مؤشر الترابط الرئيسية لأن المهمة لن تنقطع أبدًا.

فيما يلي وصف قصير لبرنامجي:تعمل المهمة على زيادة متغير الحرف (من "A" إلى "Z") وإظهاره في مؤشر ترابط واجهة المستخدم الرسومية.من أجل القيام بذلك، تقوم المهمة بتنفيذ مفوض (this.invoc()) على مؤشر الترابط الذي تم إنشاء عنصر التحكم عليه.

بمجرد أن أقوم بالتعليق على وظيفة RefreshTextBox () ، تعمل مكالمة الإلغاء وستتم مقاطعة المهمة.يبدو كما لو أن الأمر this.invoc() يمنع المهمة من المقاطعة.

أنا الكود أدناه لقد قمت أيضًا بتنفيذ نفس الوظيفة مع الخيوط العادية.وبعد ذلك أعمل.أين الفرق بين تنفيذ المهمة وتنفيذ الخيط؟

using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

public partial class frm_Main : Form
{
    private delegate void dgt_StringHandler(string str_Value);
    CancellationTokenSource _obj_Cts = null;
    Thread _obj_Thread = null;
    Task _obj_Task = null;

    public frm_Main()
    {
        InitializeComponent();
    }

    private void CreateChar(ref char chr_Value)
    {
        int int_Value;

        int_Value = (int)chr_Value;
        int_Value++;

        if (int_Value > 90 || int_Value < 65)
            int_Value = 65;

        chr_Value = (char)int_Value;
    }

    private void TestThread()
    {
        char chr_Value = '@';
        bool bol_Stop = false;

        while (!bol_Stop)
        {
            try
            {
                Thread.Sleep(300);
                CreateChar(ref chr_Value);
                RefreshTextBox(chr_Value.ToString());
            }
            catch (ThreadInterruptedException)
            {
                bol_Stop = true;
            }
        }
    }

    private void TestTask(object obj_TokenTmp)
    {
        char chr_Value = '@';
        CancellationToken obj_Token = (CancellationToken)obj_TokenTmp;

        while (!obj_Token.IsCancellationRequested)
        {
            Thread.Sleep(300);
            CreateChar(ref chr_Value);
            RefreshTextBox(chr_Value.ToString());
        }
    }

    private void RefreshTextBox(string str_Value)
    {
        if (txt_Value.InvokeRequired)
        {
            dgt_StringHandler obj_StringHandler = new dgt_StringHandler(RefreshTextBox);
            this.Invoke(obj_StringHandler, new object[] { str_Value });
        }
        else
        {
            txt_Value.Text = str_Value;
        }
    }

    private void btn_StartStop_Click(object sender, EventArgs e)
    {
        if (_obj_Task == null && _obj_Thread == null)
        {
            if (opt_Task.Checked)
            {
                _obj_Cts = new CancellationTokenSource();
                _obj_Task = new Task(new Action<object>(TestTask), _obj_Cts.Token, _obj_Cts.Token);
                _obj_Task.Start();
            }
            else
            {
                _obj_Thread = new Thread(new ThreadStart(TestThread));
                _obj_Thread.Start();
            }

            btn_StartStop.Text = "Stop";
        }
        else
        {
            if (_obj_Thread != null)
            {
                _obj_Thread.Interrupt();
                _obj_Thread.Join();
                _obj_Thread = null;
            }

            if (_obj_Task != null)
            {
                _obj_Cts.Cancel();
                _obj_Task.Wait();
                _obj_Task = null;
                _obj_Cts = null;
            }

            btn_StartStop.Text = "Start";
        }
    }
}
هل كانت مفيدة؟

المحلول

تشكل هاتان القطعتان من الكود معًا طريقًا مسدودًا:

_obj_Cts.Cancel();
_obj_Task.Wait();

و

this.Invoke(obj_StringHandler, new object[] { str_Value });

انت تتصل Wait() على مؤشر الترابط الرئيسي، ويجب معالجة Invoc() بواسطة مؤشر الترابط الرئيسي.

يمكنك كسر الجمود باستخدام this.BeginInvoke(...) بدلاً من.

يستخدم إصدار Thread المقاطعة، وهي مطرقة ثقيلة.لذلك لن يحاول الخيط الاتصال RefreshTextBox() بعد إشارة التوقف.

نصائح أخرى

ها هي الكود المعدّل.أنا الآن أتصل بـ BeginInvoke () بدلاً من Invoke () كما اقترح Henk Holterman.هذا يعمل بشكل جيد للغاية وهو الطريقة الصحيحة الوحيدة لمنع حدوث مأزق.هناك أيضًا حالة أخرى يجب النظر فيها.لدي أيضًا كائن IAsyncResult يتم توفيره من خلال استدعاء BeginInvoke ().هذا الكائن الذي أستخدمه للتحقق مما إذا كانت المكالمة غير المتزامنة قد اكتملت بالفعل أم لا.إذا لم أتحقق من ذلك ، فقد يكون مؤشر ترابط واجهة المستخدم الرسومية ليس سريعًا بدرجة كافية (على سبيل المثال بيان السكون في مكان ما في مؤشر ترابط واجهة المستخدم الرسومية) لتنفيذ المفوض الخاص بي والسبب في أن طريقة TestTask () الخاصة بي ستستدعي دائمًا BeginInvoke () على الرغم منلم يكمل مؤشر ترابط واجهة المستخدم الرسومية المفوض الأخير بالفعل.ستكون النتيجة أن مؤشر ترابط واجهة المستخدم الرسومية الخاص بي سيحظر التطبيق. Genacodicetagpre

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