c# - ললিপপ আপডেট




আমি কিভাবে অন্য থ্রেড থেকে GUI আপডেট করব? (20)

দীর্ঘ কাজ হ্যান্ডলিং

যেহেতু .NET 4.5 এবং C # 5.0 থেকে আপনি সমস্ত এলাকায় (যেমন GUI সহ) async কীওয়ার্ড সহ কার্য-ভিত্তিক অ্যাসিঙ্ক্রোনাস প্যাটার্ন (টিএপি) ব্যবহার করতে হবে:

নতুন উন্নয়ন জন্য TAP প্রস্তাবিত অ্যাসিঙ্ক্রোনাস নকশা প্যাটার্ন

পরিবর্তে অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং মডেল (এপিএম) এবং ইভেন্ট ভিত্তিক অ্যাসিঙ্ক্রোনাস প্যাটার্ন (ইএপি) (পরবর্তীতে ব্যাকগ্রাউন্ডওয়ার্কার ক্লাস অন্তর্ভুক্ত)।

তারপর, নতুন বিকাশের জন্য প্রস্তাবিত সমাধান হল:

  1. একটি ইভেন্ট হ্যান্ডলারের অ্যাসিঙ্ক্রোনাস বাস্তবায়ন (হ্যাঁ, এটি সব):

    private async void Button_Clicked(object sender, EventArgs e)
    {
        var progress = new Progress<string>(s => label.Text = s);
        await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress),
                                    TaskCreationOptions.LongRunning);
        label.Text = "completed";
    }
  2. UI থ্রেডটি সূচিত করে এমন দ্বিতীয় থ্রেডটির বাস্তবায়ন:

    class SecondThreadConcern
    {
        public static void LongWork(IProgress<string> progress)
        {
            // Perform a long running work...
            for (var i = 0; i < 10; i++)
            {
                Task.Delay(500).Wait();
                progress.Report(i.ToString());
            }
        }
    }

নিম্নলিখিত লক্ষ্য করুন:

  1. সংক্ষিপ্ত এবং পরিষ্কার কোড callbacks এবং সুস্পষ্ট থ্রেড ছাড়া ক্রমবর্ধমান পদ্ধতিতে লিখিত।
  2. Thread পরিবর্তে কাজ।
  3. async কীওয়ার্ড, যা await করতে ব্যবহার করে যা ঘটনাটি হ্যান্ডলারটিকে সমাপ্ত হওয়া পর্যন্ত সমাপ্তির অবস্থায় পৌঁছাতে বাধা দেয় এবং এর ফলে UI থ্রেডকে বাধা দেয় না।
  4. অগ্রগতির ক্লাস ( আইপ্রেস ইন্টারফেস দেখুন ) যা কনসার্নের বিচ্ছিন্নতা (SOC) নকশা নীতি সমর্থন করে এবং স্পষ্ট প্রেরক এবং উদ্ঘাটন করার প্রয়োজন হয় না। এটি বর্তমান সৃষ্টিকর্তা SynchronizationContext তার সৃষ্টি স্থান থেকে (এখানে UI থ্রেড) ব্যবহার করে।
  5. TaskCreationOptions.LongRunning যা TaskCreationOptions.LongRunning মধ্যে টাস্ক সারিবদ্ধ না নির্দেশ করে।

আরো একটি ক্রিয়াপদ প্রতিশব্দ দেখুন: সি ভবিষ্যতের #: জোসেফ Albahari দ্বারা 'অপেক্ষা' যারা ভাল জিনিস আসে

UI থ্রেডিং মডেল ধারণা সম্পর্কে আরও দেখুন।

ব্যতিক্রম হ্যান্ডলিং

ব্যাকগ্রাউন্ড নির্বাহের সময় একাধিক ক্লিকে প্রতিরোধ করার জন্য ব্যতিক্রমগুলি কীভাবে পরিচালনা করতে এবং টগল বোতামের Enabled বৈশিষ্ট্যটি হ্যান্ডেল করার নিচের নীচের স্নিপেটটি একটি উদাহরণ।

private async void Button_Click(object sender, EventArgs e)
{
    button.Enabled = false;

    try
    {
        var progress = new Progress<string>(s => button.Text = s);
        await Task.Run(() => SecondThreadConcern.FailingWork(progress));
        button.Text = "Completed";
    }
    catch(Exception exception)
    {
        button.Text = "Failed: " + exception.Message;
    }

    button.Enabled = true;
}

class SecondThreadConcern
{
    public static void FailingWork(IProgress<string> progress)
    {
        progress.Report("I will fail in...");
        Task.Delay(500).Wait();

        for (var i = 0; i < 3; i++)
        {
            progress.Report((3 - i).ToString());
            Task.Delay(500).Wait();
        }

        throw new Exception("Oops...");
    }
}

অন্য থ্রেড থেকে একটি Label আপডেট করার সবচেয়ে সহজ উপায় কি?

আমার থ্রেড 1 এ একটি Form thread1 , এবং এর থেকে আমি অন্য থ্রেড (থ্রেড thread2 ) শুরু করছি। thread2 কিছু ফাইল প্রক্রিয়াকরণ করছে যদিও আমি thread2 2 এর বর্তমান অবস্থার সাথে Form একটি Label আপডেট করতে চাই।

আমি এটা কিভাবে করবো?


.NET 2.0 এর জন্য, এখানে লেখা একটি চমৎকার বিট কোড যা আপনি চান তা ঠিক করে এবং Control কোনও সম্পত্তিটির জন্য কাজ করে:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);

public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }
}

এটির মতো কল করুন:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

আপনি যদি .NET 3.0 বা তার উপরে ব্যবহার করেন, তবে আপনি উপরের পদ্ধতিটিকে Control ক্লাসের এক্সটেনশন পদ্ধতি হিসাবে পুনর্লিখন করতে পারেন, যা তখন কলটিকে সরল করে তুলবে:

myLabel.SetPropertyThreadSafe("Text", status);

আপডেট 05/10/2010:

.NET 3.0 এর জন্য আপনাকে এই কোডটি ব্যবহার করতে হবে:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);

public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

যা অনেক ক্লিনার, সরল এবং নিরাপদ সিনট্যাক্সকে অনুমোদন করার জন্য LINQ এবং Lambda এক্সপ্রেশনগুলি ব্যবহার করে:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

এখন কম্পাইল সময়টিতে চেক করা সম্পত্তি নামটি নয়, সম্পত্তির ধরনটিও ভাল, তাই এটি অসম্ভব (উদাহরণস্বরূপ) একটি বুলিয়ান সম্পত্তিটির স্ট্রিং মান বরাদ্দ করা এবং এটি একটি রানটাইম ব্যতিক্রম হতে পারে।

দুর্ভাগ্যবশত এটি অন্য Control এর সম্পত্তি এবং মান পাশাপাশি মূঢ় জিনিস করছেন যে কেউ থামাতে না, তাই নিম্নলিখিত happily কম্পাইল হবে:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

তাই আমি রানটাইম চেকগুলিকে যোগ করেছিলাম যাতে নিশ্চিত করা যায় যে পাস-ইন সম্পত্তি প্রকৃতপক্ষে Control অন্তর্গত যা পদ্ধতিটি বলা হচ্ছে। নিখুঁত নয়, তবে এখনও .NET 2.0 সংস্করণের চেয়ে অনেক ভাল।

কম্পাইল-টাইম নিরাপত্তার জন্য এই কোডটি কীভাবে উন্নত করতে হবে তার বিষয়ে কেউ যদি আরও পরামর্শ দেয় তবে দয়া করে মন্তব্য করুন!


Salvete! এই প্রশ্নের জন্য অনুসন্ধান করা হলে, আমার কাছে ফ্র্যাঙ্ক জি এবং ওরেগন ঘোস্টের উত্তরগুলি সবচেয়ে সহজতম আমার কাছে সবচেয়ে সহজ। এখন, আমি ভিসুয়াল বেসিক কোড এবং একটি কনভার্টার মাধ্যমে এই স্নিপেট দৌড়ে; তাই আমি এটা সক্রিয় আউট কিভাবে বেশ নিশ্চিত নই।

আমার একটি ডায়ালগ ফর্ম আছে যা form_Diagnostics, নামে form_Diagnostics, যার একটি richtext বক্স আছে, যা updateDiagWindow, নামে updateDiagWindow, যা আমি লগিং প্রদর্শনের একটি ধরণের হিসাবে ব্যবহার করছি। আমি সব থ্রেড থেকে তার টেক্সট আপডেট করতে সক্ষম হতে হবে। অতিরিক্ত লাইন উইন্ডো স্বয়ংক্রিয়ভাবে নতুন লাইন স্ক্রোল করতে অনুমতি দেয়।

এবং তাই, আমি এখন এক লাইনের সাথে প্রদর্শনটি আপডেট করতে পারি, সমগ্র প্রোগ্রামে যে কোন জায়গায় যেভাবে আপনি মনে করেন যে এটি কোনও থ্রেডিং ছাড়াই কাজ করবে:

  form_Diagnostics.updateDiagWindow(whatmessage);

মুখ্য কোড (আপনার ফর্মের ক্লাস কোডের ভিতরে এটি রাখুন):

#region "---------Update Diag Window Text------------------------------------"
// This sub allows the diag window to be updated by all threads
public void updateDiagWindow(string whatmessage)
{
    var _with1 = diagwindow;
    if (_with1.InvokeRequired) {
        _with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage);
    } else {
        UpdateDiag(whatmessage);
    }
}
// This next line makes the private UpdateDiagWindow available to all threads
private delegate void UpdateDiagDelegate(string whatmessage);
private void UpdateDiag(string whatmessage)
{
    var _with2 = diagwindow;
    _with2.appendtext(whatmessage);
    _with2.SelectionStart = _with2.Text.Length;
    _with2.ScrollToCaret();
}
#endregion

অনেক উদ্দেশ্যে এটির মতো সহজ:

public delegate void serviceGUIDelegate();
private void updateGUI()
{
  this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI ()" ফর্মের মধ্যে একটি GUI লেভেল পদ্ধতি (এটি) যা আপনি চান হিসাবে অনেক নিয়ন্ত্রণ পরিবর্তন করতে পারেন। অন্যান্য থ্রেড থেকে "updateGUI ()" কল করুন। প্যারামিটারগুলিকে মানগুলি পাস করতে যুক্ত করা যেতে পারে, অথবা (সম্ভবত দ্রুত) শ্রেণীকক্ষের ভেরিয়েবলগুলিকে তাদের উপর তালা দিয়ে ব্যবহার করতে হবে, যদি অস্থিরতার কারণে তাদের অ্যাক্সেসের থ্রেডগুলির মধ্যে সংঘর্ষের সম্ভাবনা থাকে। অ-জিআইআই থ্রেডটি সমালোচনামূলক সময় (মনের মধ্যে ব্রায়ান গিদিয়োনের সতর্কতা অবলম্বন করার সময়) যদি Invinok এর পরিবর্তে BeginInvoke ব্যবহার করুন।


আপনি সঠিক থ্রেড আপডেট আপডেট ঘটতে হবে তা নিশ্চিত করতে হবে; UI থ্রেড।

এটি করার জন্য, আপনাকে সরাসরি কল করার পরিবর্তে ইভেন্ট-হ্যান্ডলারকে আমন্ত্রণ জানাতে হবে।

আপনি এভাবে আপনার ইভেন্টটি উত্থাপন করে এটি করতে পারেন:

(কোডটি আমার মাথার বাইরে টাইপ করা হয়েছে, তাই আমি সঠিক সিনট্যাক্স ইত্যাদি চেক করেছি না, তবে এটি আপনাকে যেতে হবে।)

if( MyEvent != null )
{
   Delegate[] eventHandlers = MyEvent.GetInvocationList();

   foreach( Delegate d in eventHandlers )
   {
      // Check whether the target of the delegate implements 
      // ISynchronizeInvoke (Winforms controls do), and see
      // if a context-switch is required.
      ISynchronizeInvoke target = d.Target as ISynchronizeInvoke;

      if( target != null && target.InvokeRequired )
      {
         target.Invoke (d, ... );
      }
      else
      {
          d.DynamicInvoke ( ... );
      }
   }
}

উল্লেখ্য যে উপরের কোডটি WPF প্রকল্পগুলিতে কাজ করবে না, যেহেতু WPF কন্ট্রোলগুলি ISynchronizeInvoke ইন্টারফেসটি বাস্তবায়ন করে না।

উপরের কোডটি উইন্ডোজ ফর্ম এবং ডাব্লুপিএফ এবং অন্যান্য সমস্ত প্ল্যাটফর্মগুলির সাথে কাজ করে তা নিশ্চিত করতে, আপনি AsyncOperation , AsyncOperationManager এবং SynchronizationContext ক্লাসগুলির দিকে নজর রাখতে পারেন।

এইভাবে ঘটনাগুলি সহজে বাড়াতে, আমি একটি এক্সটেনশান পদ্ধতি তৈরি করেছি, যা আমাকে কল করার মাধ্যমে একটি ইভেন্ট উত্থাপন সহজ করতে দেয়:

MyEvent.Raise(this, EventArgs.Empty);

অবশ্যই, আপনি ব্যাকগ্রাউন্ডওয়ার্কার ক্লাস ব্যবহার করতে পারেন, যা আপনার জন্য এই বিষয়টি বিমূর্ত করবে।


ইয়ান কেমসের সমাধানগুলির আমার সি # 3.0 বৈচিত্রের মধ্যে এটি:

public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

আপনি এটি এই মত কল:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. এটি "MemberExpression হিসাবে" ফলাফলের জন্য নিল-পরীক্ষণ যোগ করে।
  2. এটি স্ট্যাটিক টাইপ-নিরাপত্তা উন্নত।

অন্যথায়, মূল একটি খুব সুন্দর সমাধান।


এই ক্লাসিক উপায় আপনি এই করা উচিত:

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

namespace Test
{
    public partial class UIThread : Form
    {
        Worker worker;

        Thread workerThread;

        public UIThread()
        {
            InitializeComponent();

            worker = new Worker();
            worker.ProgressChanged += new EventHandler<ProgressChangedArgs>(OnWorkerProgressChanged);
            workerThread = new Thread(new ThreadStart(worker.StartWork));
            workerThread.Start();
        }

        private void OnWorkerProgressChanged(object sender, ProgressChangedArgs e)
        {
            // Cross thread - so you don't get the cross-threading exception
            if (this.InvokeRequired)
            {
                this.BeginInvoke((MethodInvoker)delegate
                {
                    OnWorkerProgressChanged(sender, e);
                });
                return;
            }

            // Change control
            this.label1.Text = e.Progress;
        }
    }

    public class Worker
    {
        public event EventHandler<ProgressChangedArgs> ProgressChanged;

        protected void OnProgressChanged(ProgressChangedArgs e)
        {
            if(ProgressChanged!=null)
            {
                ProgressChanged(this,e);
            }
        }

        public void StartWork()
        {
            Thread.Sleep(100);
            OnProgressChanged(new ProgressChangedArgs("Progress Changed"));
            Thread.Sleep(100);
        }
    }


    public class ProgressChangedArgs : EventArgs
    {
        public string Progress {get;private set;}
        public ProgressChangedArgs(string progress)
        {
            Progress = progress;
        }
    }
}

আপনার কর্মী থ্রেড একটি ঘটনা আছে। আপনার ইউআই থ্রেড কাজটি করার জন্য অন্য থ্রেড বন্ধ করে এবং সেই কর্মী ইভেন্টটিকে হুক আপ করে যাতে আপনি শ্রমিক থ্রেডের অবস্থা প্রদর্শন করতে পারেন।

তারপরে UI এ আপনাকে প্রকৃত নিয়ন্ত্রণ পরিবর্তন করার জন্য থ্রেডগুলি ক্রস করতে হবে ... লেবেল বা অগ্রগতি বারের মত।


এটি একটি। নেট ফ্রেমওয়ার্ক 3.0 ব্যবহার করে উপরে সমাধান হিসাবে অনুরূপ, তবে এটি কম্পাইল-টাইম সুরক্ষা সমর্থনের সমস্যা সমাধান করেছে।

public  static class ControlExtension
{
    delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value);

    public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value)
    {
        if (source.InvokeRequired)
        {
            var del = new SetPropertyValueHandler<TResult>(SetPropertyValue);
            source.Invoke(del, new object[]{ source, selector, value});
        }
        else
        {
            var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo;
            propInfo.SetValue(source, value, null);
        }
    }
}

ব্যবহার করা:

this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string");
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false);

ব্যবহারকারী ভুল তথ্য টাইপ পাস করলে কম্পাইলার ব্যর্থ হবে।

this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext");

দৃশ্যের ত্রৈমাসিকতার কারণে আমি আসলে স্ট্যাটাসের জন্য UI থ্রেড পোল রাখব। আমি আপনি বেশ মার্জিত হতে পারে যে পাবেন।

public class MyForm : Form
{
  private volatile string m_Text = "";
  private System.Timers.Timer m_Timer;

  private MyForm()
  {
    m_Timer = new System.Timers.Timer();
    m_Timer.SynchronizingObject = this;
    m_Timer.Interval = 1000;
    m_Timer.Elapsed += (s, a) => { MyProgressLabel.Text = m_Text; };
    m_Timer.Start();
    var thread = new Thread(WorkerThread);
    thread.Start();
  }

  private void WorkerThread()
  {
    while (...)
    {
      // Periodically publish progress information.
      m_Text = "Still working...";
    }
  }
}

পদ্ধতিটি ISynchronizeInvoke.Invoke এবং ISynchronizeInvoke.BeginInvoke পদ্ধতি ব্যবহার করার সময় প্রয়োজনীয় মার্শালিং ক্রিয়াকলাপ এড়ানো হয়। মার্শালিং কৌশল ব্যবহার করার ক্ষেত্রে কোনও ভুল নেই, তবে কয়েকটি সতর্কতা রয়েছে যা আপনাকে সচেতন হতে হবে।

  • আপনি BeginInvoke খুব ঘনঘন কল করবেন না তা নিশ্চিত করুন অথবা এটি বার্তা পাম্প overrun হতে পারে।
  • কর্মীদের থ্রেডে কল করার আহ্বান একটি ব্লকিং কল। এটা সাময়িকভাবে যে থ্রেড কাজ সম্পন্ন করা থামাতে হবে।

আমি এই প্রশ্নের প্রস্তাব প্রস্তাব থ্রেডের যোগাযোগ ভূমিকা বিপরীত। তার পরিবর্তে ব্যবহারকারীর থ্রেডটির জন্য UI থ্রেড পোল ডেটা ধাক্কা দেয়। এটি একটি সাধারণ প্যাটার্ন অনেক পরিস্থিতিতে ব্যবহৃত। যেহেতু আপনি যা করতে চান তা হল কর্মী থ্রেড থেকে অগ্রগতির তথ্য প্রদর্শন করা, তাই আমি মনে করি আপনি এই সমাধানটি মার্শালিং সমাধানটির একটি দুর্দান্ত বিকল্প পাবেন। এটা নিম্নলিখিত সুবিধার আছে।

  • ইউআই এবং ওয়ার্কার থ্রেডগুলি Control.Invoke বা Control.BeginInvoke পদ্ধতির বিরোধিতা করে যা দৃঢ়ভাবে তাদের সাথে যুক্ত করে।
  • ইউআই থ্রেড কর্মী থ্রেড অগ্রগতি বাধা দেবে না।
  • UI থ্রেড আপডেট করার সময় ব্যবহারকারীর থ্রেড আয়ত্ত করতে পারে না।
  • UI এবং কর্মী থ্রেডগুলি ক্রিয়াকলাপগুলি সম্পাদন করে এমন অন্তরগুলি স্বাধীন থাকতে পারে।
  • কর্মী থ্রেড UI থ্রেড এর বার্তা পাম্প overrun করতে পারে না।
  • ইউআই থ্রেড কখন এবং কিভাবে প্রায়শই আপডেট করা হয় তা নির্দেশ করতে পায়।

পূর্ববর্তী উত্তর স্টাফ Invoke কোন প্রয়োজন নেই।

আপনি WindowsFormsSynchronizationContext এ দেখতে হবে:

// In the main thread
WindowsFormsSynchronizationContext mUiContext = new WindowsFormsSynchronizationContext();

...

// In some non-UI Thread

// Causes an update in the GUI thread.
mUiContext.Post(UpdateGUI, userData);

...

void UpdateGUI(object userData)
{
    // Update your GUI controls here
}

সবচেয়ে সরল উপায় একটি বেনামী পদ্ধতি Label.Invoke মধ্যে পাস করা Label.Invoke :

// Running on the worker thread
string newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {
    // Running on the UI thread
    form.Label.Text = newText;
});
// Back on the worker thread

লক্ষ্য করুন যে এটি সম্পূর্ণ না হওয়া পর্যন্ত ব্লক Invoke করুন - এটি সিঙ্ক্রোনাস কোড। প্রশ্নটি অ্যাসিঙ্ক্রোনাস কোড সম্পর্কে জিজ্ঞাসা করে না, তবে স্ট্যাক ওভারফ্লোতে এ্যাক্রিনক্রোনাস কোড লেখার বিষয়ে প্রচুর সামগ্রী রয়েছে যখন আপনি এটি সম্পর্কে জানতে চান।


সহজ সমাধান Control.Invoke ব্যবহার করা হয়।

void DoSomething()
{
    if (InvokeRequired) {
        Invoke(new MethodInvoker(updateGUI));
    } else {
        // Do Something
        updateGUI();
    }
}

void updateGUI() {
    // update gui here
}

আমি মনে করি সবচেয়ে সহজ উপায়:

   void Update()
   {
       BeginInvoke((Action)delegate()
       {
           //do your update
       });
   }

উদাহরণস্বরূপ, বর্তমান থ্রেড ছাড়া অন্য কোনও নিয়ন্ত্রণ অ্যাক্সেস করুন:

Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
    lblThreshold.Text = Speed_Threshold.ToString();
}));

সেখানে lblThresholdএকটি লেবেল এবং Speed_Thresholdএকটি গ্লোবাল পরিবর্তনশীল।


একটি ক্লাস পরিবর্তনশীল তৈরি করুন:

SynchronizationContext _context;

আপনার UI তৈরি করে এমন কন্সট্রকটারে এটি সেট করুন:

var _context = SynchronizationContext.Current;

যখন আপনি লেবেল আপডেট করতে চান:

_context.Send(status =>{
    // UPDATE LABEL
}, null);

শুধু এই মত কিছু ব্যবহার করুন:

 this.Invoke((MethodInvoker)delegate
            {
                progressBar1.Value = e.ProgressPercentage; // runs on UI thread
            });

আমার সংস্করণ একটি recursive "মন্ত্র" লাইন সন্নিবেশ করা হয় :

কোন আর্গুমেন্ট জন্য:

    void Aaaaaaa()
    {
        if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra

        // Your code!
    }

আর্গুমেন্ট আছে যে একটি ফাংশন জন্য:

    void Bbb(int x, string text)
    {
        if (InvokeRequired) { Invoke(new Action<int, string>(Bbb), new[] { x, text }); return; }
        // Your code!
    }

এটা আইটি

কিছু যুক্তি : সাধারণত এটি if ()একটি লাইনের বিবৃতির পরে {} রাখা কোড পঠনযোগ্যতার জন্য খারাপ । কিন্তু এই ক্ষেত্রে এটি রুটিন সব একই একই "মন্ত্র"। এই পদ্ধতি প্রকল্পে সামঞ্জস্যপূর্ণ যদি এটি কোড পঠনযোগ্যতা বিরতি না। এবং এটি আপনার কোডকে ঠকানো থেকে রক্ষা করে (পাঁচটি পরিবর্তে কোডের একটি লাইন)।

আপনি দেখতে হিসাবে আপনি if(InvokeRequired) {something long}শুধু "এই ফাংশন অন্য থ্রেড থেকে কল নিরাপদ" জানি।


আমি একটি সতর্কতা যোগ করতে চেয়েছিলাম কারণ আমি লক্ষ্য করেছি যে কিছু সহজ সমাধান InvokeRequiredচেকটি বাদ দেয় ।

আমি লক্ষ্য করেছি যে যদি আপনার কোড নিয়ন্ত্রণের উইন্ডো হ্যান্ডেলটি আগে সঞ্চালিত হয় (যেমন ফর্মটি দেখানোর আগে), Invokeএকটি ব্যতিক্রম ছুঁড়ে ফেলে। তাই আমি সর্বদা InvokeRequiredকলিং আগে Invokeবা চেক করার সুপারিশ BeginInvoke


যখন আপনি UI থ্রেডে থাকবেন তখন আপনি এটির সিঙ্ক্রোনাইজেশান প্রসঙ্গ টাস্ক সময় নির্ধারণকারীর জন্য এটি চাইতে পারেন। এটি আপনাকে একটি TaskScheduler যা UI থ্রেডে সবকিছুই নির্ধারণ করবে।

তারপরে আপনি আপনার কাজগুলি শৃঙ্খল করতে পারেন যাতে ফলাফলটি প্রস্তুত হলে অন্য একটি টাস্ক (যা UI থ্রেডে নির্ধারিত হয়) এটি পছন্দ করে এবং এটি লেবেলে নির্ধারিত করে।

public partial class MyForm : Form
{
  private readonly TaskScheduler _uiTaskScheduler;
  public MyForm()
  {
    InitializeComponent();
    _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
  }

  private void buttonRunAsyncOperation_Click(object sender, EventArgs e)
  {
    RunAsyncOperation();
  }

  private void RunAsyncOperation()
  {
    var task = new Task<string>(LengthyComputation);
    task.ContinueWith(antecedent =>
                         UpdateResultLabel(antecedent.Result), _uiTaskScheduler);
    task.Start();
  }

  private string LengthyComputation()
  {
    Thread.Sleep(3000);
    return "47";
  }

  private void UpdateResultLabel(string text)
  {
    labelResult.Text = text;
  }
}

এটি কাজগুলির জন্য কাজ করে (থ্রেড নয়) যা এখন সমবর্তী কোড লেখার পছন্দের উপায়


যখন আমি একই সমস্যার মুখোমুখি হই, তখন আমি গুগল থেকে সাহায্য চেয়েছিলাম, বরং আমাকে একটি সহজ সমাধান দেওয়ার পরিবর্তে এটি আমাকে আরও বেশি বিভ্রান্ত করে MethodInvokerতুলেছিল এবং উদাহরণস্বরূপ। তাই আমি নিজের উপর এটি সমাধান করার সিদ্ধান্ত নিয়েছে। এখানে আমার সমাধান:

এই মত একটি প্রতিনিধি তৈরি করুন:

Public delegate void LabelDelegate(string s);

void Updatelabel(string text)
{
   if (label.InvokeRequired)
   {
       LabelDelegate LDEL = new LabelDelegate(Updatelabel);
       label.Invoke(LDEL, text);
   }
   else
       label.Text = text
}

আপনি এই ফাংশন এই মত একটি নতুন থ্রেড কল করতে পারেন

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

সঙ্গে বিভ্রান্ত করা হবে না Thread(() => .....)। আমি একটি থ্রেড কাজ যখন আমি একটি বেনামী ফাংশন বা Lambda অভিব্যক্তি ব্যবহার। কোড লাইন হ্রাস করার জন্য আপনি ThreadStart(..)পদ্ধতিটিও ব্যবহার করতে পারেন যা আমি এখানে ব্যাখ্যা করতে চাই না।







user-interface