আমি অন্য থ্রেড থেকে জিইউআই কীভাবে আপডেট করব?


1392

Labelঅন্যের কাছ থেকে কোনও আপডেট করার সহজ উপায় কোনটি Thread?

  • আমার একটি Formচলমান আছে thread1এবং সে থেকে আমি অন্য থ্রেড শুরু করছি ( thread2))

  • যদিও thread2কিছু ফাইল প্রক্রিয়াকরণ হচ্ছে আমি একটি আপডেট করতে চান Labelউপর Formবর্তমান অবস্থা সঙ্গে thread2এর কর্মক্ষেত্র।

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


25
.NET 2.0+ এর জন্য কেবল ব্যাকগ্রাউন্ড ওয়ার্কার ক্লাস নেই। এটি UI থ্রেড সচেতন। 1. একটি পটভূমি ওয়ার্কার তৈরি করুন 2. দুটি প্রতিনিধি যুক্ত করুন (একটি প্রক্রিয়াজাতকরণের জন্য, এবং একটি সমাপ্তির জন্য)
প্রীত সংঘ

13
হয়তো কিছুটা দেরি হয়েছে: কোডপ্রোজন
মাইকেল

4
: .NET 4.5 এবং C # 5.0 জন্য উত্তর দেখুন stackoverflow.com/a/18033198/2042090
Ryszard Dżegan

5
এই প্রশ্নটি Gtk # GUI- তে প্রযোজ্য নয়। Gtk # এর জন্য এটি এবং এই উত্তরটি দেখুন।
hlovdal

সাবধান: এই প্রশ্নের উত্তরগুলি এখন ওটি ("আমি এখানে আমার ডাব্লুপিএফ অ্যাপ্লিকেশনটির জন্য কী করেছি") এবং historicalতিহাসিক নেট .০.০ শিল্পকর্মের বিশৃঙ্খলা সৃষ্টি করে।
মার্ক এল।

উত্তর:


768

.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);

আপনি যদি নেট নেট 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 });
  }
}

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

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

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

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

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

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

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


3
এমন কেস রয়েছে যখন এটি.গেটটাইপ () প্রপার্টিআইএনফো.রিফ্লেটেডটাইপ (যেমন উইনফোর্মে লিঙ্কলবেল) হিসাবে একই মূল্যায়ন করে। আমার কাছে বড় সি # অভিজ্ঞতা নেই, তবে আমি মনে করি যে ব্যতিক্রমের শর্তটি এমন হওয়া উচিত: যদি (সম্পত্তিInfo == নাল || (!! এই গেটটাইপ () Sসসব্লक्সঅফ (সম্পত্তিInfo.RefectedType) && this.GetType ( )! = সম্পত্তিInfo.RefectedType) || @ this.GetType ()। getProperty (সম্পত্তিInfo.Name, संपत्तिInfo.PropertyType) == নাল)
করভিন

9
@ এলান SetControlPropertyThreadSafe(myLabel, "Text", status)এটিকে অন্য মডিউল বা শ্রেণি বা ফর্ম থেকে কল করা যেতে পারে
স্মিথ

71
প্রদত্ত সমাধানটি অহেতুক জটিল। আপনি যদি সরলতার মূল্য দেন তবে মার্ক গ্রাভেলের সমাধান বা জায়েদ মাসুদের সমাধান দেখুন।
ফ্রাঙ্ক হিলিমান

8
আপনি যদি প্রতিটি ইনভোকের জন্য প্রচুর সংস্থান ব্যয় করে একাধিক সম্পত্তি আপডেট করেন তবে এই সমাধানটি টনের সংস্থানগুলি অপচয় করে না। আমি মনে করি না যে থ্রেড সুরক্ষাটির বৈশিষ্ট্যটি যেভাবেই করা হয়েছিল। আপনার ইউআই আপডেটের ক্রিয়াকলাপগুলি কীভাবে encapsulte করুন এবং এটি একবারে চালু করুন (এবং সম্পত্তি হিসাবে নয়)
কনসোল

4
কেন পৃথিবীতে আপনি এই কোডটি ব্যাকগ্রাউন্ড ওয়ার্কার উপাদানটির মাধ্যমে ব্যবহার করবেন?
অ্যান্ডি

1077

সবচেয়ে সহজ উপায় হ'ল একটি বেনাম পদ্ধতি যা এর মধ্যে প্রবেশ করা হয়েছে 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এটি কার্যকর না হওয়া অবধি কার্যকর করা অবরুদ্ধ করে - এটি সিঙ্ক্রোনাস কোড। প্রশ্নটি অ্যাসিঙ্ক্রোনাস কোড সম্পর্কে জিজ্ঞাসা করে না, তবে অ্যাসিক্রোনাস কোড লেখার বিষয়ে স্ট্যাক ওভারফ্লোতে প্রচুর সামগ্রী রয়েছে যখন আপনি এটি সম্পর্কে জানতে চান।


8
ওপি হিসাবে ফর্ম ব্যতীত কোনও শ্রেণি / উদাহরণ উল্লেখ করা হয়নি , এটি খারাপ ডিফল্ট নয় ...
মার্ক গ্র্যাভেল

39
"এই" কীওয়ার্ডটি একটি "নিয়ন্ত্রণ" শ্রেণিটি উল্লেখ করছে তা ভুলে যাবেন না।
এজেড

8
@ কোডকম্প্লেটিং এটি যে কোনও উপায়েই নিরাপদ, এবং আমরা ইতিমধ্যে জানি যে আমরা একজন কর্মী, সুতরাং কেন আমরা কিছু জানি কেন তা পরীক্ষা করব?
মার্ক গ্র্যাভেল

4
@ ড্রাগাউফ সত্যই নয় - এই পদ্ধতিটি ব্যবহার করার একটি বিষয় হ'ল আপনি ইতিমধ্যে জানেন যে কোন অংশটি কর্মীর উপর চলে এবং কোনটি ইউআই থ্রেডে চালিত হয়। চেক করার দরকার নেই।
মার্ক Gravell

3
@ জোয়ান.বিডিএম আমার পক্ষে এ সম্পর্কে মন্তব্য করার মতো যথেষ্ট প্রসঙ্গের কাছাকাছি কোথাও নেই
মার্ক গ্রাভেল

399

দীর্ঘ কাজ পরিচালনা

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

ট্যাপ নতুন বিকাশের জন্য প্রস্তাবিত অ্যাসিনক্রোনাস ডিজাইনের ধরণ pattern

পরিবর্তে অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং মডেল (APM) এবং ইভেন্ট ভিত্তিক অ্যাসিঙ্ক্রোনাস প্যাটার্ন (ইএপি) (আধুনিক অন্তর্ভুক্ত BackgroundWorker ক্লাস )।

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

  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. ইউআই থ্রেডকে সূচিত করে এমন দ্বিতীয় থ্রেডের বাস্তবায়ন:

    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. কলব্যাক এবং সুস্পষ্ট থ্রেড ছাড়াই অনুক্রমিক পদ্ধতিতে লিখিত শর্ট এবং ক্লিন কোড।
  2. থ্রেডের পরিবর্তে টাস্ক
  3. অ্যাসিঙ্ক কীওয়ার্ড, এটি অপেক্ষা করার সুযোগ দেয় যা ফলস্বরূপ ইভেন্ট হ্যান্ডলারটি টাস্ক শেষ না হওয়া অবধি সমাপ্তির রাজ্যে পৌঁছতে বাধা দেয় এবং এর মধ্যে ইউআই থ্রেডকে অবরুদ্ধ করে না।
  4. অগ্রগতি শ্রেণি ( আইপোগ্রেস ইন্টারফেস দেখুন ) যা কনসার্নস বিচ্ছিন্নকরণ (এসওসি) ডিজাইন নীতি সমর্থন করে এবং সুস্পষ্ট প্রেরণকারী এবং চিত্তাকর্ষক প্রয়োজন হয় না। এটি তার তৈরি স্থান (এখানে ইউআই থ্রেড) থেকে বর্তমান সিঙ্ক্রোনাইজেশন কনটেক্সট ব্যবহার করে ।
  5. TaskCreationOptions.LongRunning যে নির্দেশ মধ্যে টাস্ক সারিতে না ThreadPool

আরও শব্দাবলীর উদাহরণগুলির জন্য দেখুন: সি # এর ভবিষ্যত: যারা জোসেফ আলবাহারি দ্বারা 'অপেক্ষা' করেন তাদের কাছে ভাল জিনিস আসে

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

ব্যতিক্রম পরিচালনা

নীচের স্নিপেটটি 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...");
    }
}

2
যদি SecondThreadConcern.LongWork()কোনও ব্যতিক্রম ছুঁড়ে, এটি কি ইউআই থ্রেডের দ্বারা ধরা পড়তে পারে? এটি একটি দুর্দান্ত পোস্ট, বিটিডব্লিউ।
kdbanman

2
আপনার প্রয়োজনীয়তা পূরণ করতে আমি উত্তরে একটি অতিরিক্ত বিভাগ যুক্ত করেছি। শুভেচ্ছা।
Ryszard Dżegan

3
ExceptionDispatchInfo বর্গ ASYNC-অপেক্ষায় রয়েছেন প্যাটার্ন UI 'তে থ্রেডে পটভূমি ব্যতিক্রম rethrowing যে অলৌকিক ঘটনা জন্য দায়ী।
Ryszard Dżegan

1
এটা কি কেবল এই ভেবে বসে থাকি যে এই উপায়টি কেবল ইনভোক / শুরু করার চেয়ে আরও বেশি ভার্বোস ?!
মেটিটাস

2
Task.Delay(500).Wait()? কেবলমাত্র বর্তমান থ্রেডটি ব্লক করার জন্য একটি টাস্ক তৈরি করার মূল বিষয় কী? আপনার কোনও থ্রেড পুলের থ্রেডটি ব্লক করা উচিত নয়!
ইয়ারিক

236

নেট 4: এর জন্য মার্ক গ্রাভেলের সহজ সমাধানের প্রকরণ ation

control.Invoke((MethodInvoker) (() => control.Text = "new text"));

অথবা পরিবর্তে অ্যাকশন প্রতিনিধি ব্যবহার করুন:

control.Invoke(new Action(() => control.Text = "new text"));

দুটির তুলনার জন্য এখানে দেখুন: মেথডইনভোকার বনাম অ্যাকশন ফর কন্ট্রোল.বেগিনআইভোক


1
এই উদাহরণে 'নিয়ন্ত্রণ' কী? আমার ইউআই নিয়ন্ত্রণ? এটি একটি লেবেল নিয়ন্ত্রণে ডাব্লুপিএফ এ প্রয়োগ করার চেষ্টা করছে এবং ইনভোক আমার লেবেলের সদস্য নয়।
ডাব্লুম

সম্পর্কে কি এক্সটেনশন পদ্ধতি @styxriver মত stackoverflow.com/a/3588137/206730 ?
কুইকিনেট

'অ্যাকশন y;' ঘোষণা করুন শ্রেণীর বা পদ্ধতির অভ্যন্তরে পাঠ্য সম্পত্তিটি পরিবর্তন করে এই টুকরো কোডটি 'আপনারকন্ট্রোল' দিয়ে পাঠ্য আপডেট করুন nআনভোক করুন (y = () => আপনারকন্ট্রোল T
আন্তোনিও লাইট

4
@ ব্লুম এটি সদস্য নয় কারণ এটি কেবল উইনফর্মের জন্য।
ডাব্লুপিএফ-এর

1
আমি এই সমাধানটি অনুসরণ করছিলাম তবে কখনও কখনও আমার ইউআই আপডেট হয় না। আমি দেখতে পেয়েছি যে this.refresh()জিইআইআইকে জোর করে অবৈধ করা এবং পুনরায় রঙ করা দরকার .. এটি যদি সহায়ক হয় ..
রকিবুল হক

137

.NET 3.5+ এর জন্য এক্সটেনশন পদ্ধতিতে আগুন এবং ভুলে যান

using System;
using System.Windows.Forms;

public static class ControlExtensions
{
    /// <summary>
    /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
    /// </summary>
    /// <param name="control"></param>
    /// <param name="code"></param>
    public static void UIThread(this Control @this, Action code)
    {
        if (@this.InvokeRequired)
        {
            @this.BeginInvoke(code);
        }
        else
        {
            code.Invoke();
        }
    }
}

নিম্নলিখিত কোডের লাইনটি ব্যবহার করে এটিকে বলা যেতে পারে:

this.UIThread(() => this.myLabel.Text = "Text Goes Here");

5
এই ব্যবহারের মূল বিষয় কী? "নিয়ন্ত্রণ" সমতুল্য হবে না? এটির কি কোনও সুবিধা আছে?
আরজিলে

14
@ জিরোমায়ার্স - @thisকেবলমাত্র পরিবর্তনশীল নাম, এক্ষেত্রে এক্সটেনশানটিকে কল করার বর্তমান নিয়ন্ত্রণের উল্লেখ। আপনি এটি উত্স, বা আপনার নৌকা ভাসমান যা কিছু পরিবর্তন করতে পারেন। আমি ব্যবহার করি @this, কারণ এটি 'এই নিয়ন্ত্রণ' উল্লেখ করে যা এক্সটেনশানটি কল করে এবং স্বাভাবিক (অ-এক্সটেনশন) কোডে 'এই' কীওয়ার্ডটি ব্যবহার করার সাথে সামঞ্জস্যপূর্ণ (আমার মাথায়, অন্তত) consistent
স্টাইক্রাইভার

1
এটি দুর্দান্ত, সহজ এবং আমার পক্ষে সেরা সমাধান। ইউআই থ্রেডে আপনাকে যা করতে হবে তা আপনি অন্তর্ভুক্ত করতে পারেন। উদাহরণ: this.UIThread (() => xt txtMessage.Text = message; listBox1.Items.Add (বার্তা);});
অটো

1
আমি এই সমাধানটি সত্যিই পছন্দ করি। মাইনর নিট: আমি এই পদ্ধতির নাম হবে OnUIThreadবরং UIThread
টুলমেকারস্টেভ

2
এজন্য আমি এই এক্সটেনশনের নাম রেখেছি RunOnUiThread। তবে এটি কেবল ব্যক্তিগত স্বাদে।
গ্রিসগ্রাম

66

এটি আপনার সর্বোত্তমভাবে করা উচিত:

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;
        }
    }
}

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

তারপরে ইউআইতে আপনার লেবেল বা অগ্রগতির বারের মতো আসল নিয়ন্ত্রণ পরিবর্তন করতে থ্রেডগুলি অতিক্রম করতে হবে।


62

এর সহজ সমাধানটি ব্যবহার করা Control.Invoke

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

void updateGUI() {
    // update gui here
}

সরলতার জন্য ভাল কাজ! না শুধুমাত্র সহজ, কিন্তু ভাল কাজ করে! মাইক্রোসফ্ট কেন বোঝানো হচ্ছে তাই এটিকে কেন সহজ করে তুলতে পারল না তা আমি সত্যিই বুঝতে পারি নি! মূল থ্রেডে 1 লাইন কল করার জন্য, আমাদের কয়েকটি ফাংশন লিখতে হবে!
এমবিএইচ

1
@ এমবিএইচ সম্মত। বিটিডাব্লু, আপনি কি উপরে স্ট্যাকওভারফ্লো.com/a/3588137/199364 উত্তর লক্ষ্য করেছেন , যা কোনও এক্সটেনশন পদ্ধতি সংজ্ঞায়িত করে? কাস্টম ইউটিলিটি ক্লাসে একবার এটি করুন, তারপরে মাইক্রোসফ্ট এটি আমাদের জন্য না করে এমন আর কোনও যত্ন নেওয়ার দরকার নেই :)
টুলমেকারস্টেভ

@ টুলমেকারস্টেটিভ ঠিক কী বোঝাতে চেয়েছেন তা! আপনি ঠিক বলেছেন আমরা একটি উপায় খুঁজে পেতে পারি, তবে আমার অর্থ ডিআরওয়াই (নিজেকে পুনরাবৃত্তি করবেন না) দৃষ্টিকোণ থেকে, যে সমস্যাটির সাধারণ সমাধান রয়েছে, তাদের মাইক্রোসফ্ট দ্বারা ন্যূনতম প্রচেষ্টা দিয়ে সমাধান করা যেতে পারে যার জন্য অনেক সময় সাশ্রয় হবে প্রোগ্রামার্স :)
এমবিএইচ

47

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

    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "A");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "B");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "C");
    }

    private void backgroundWorker1_ProgressChanged(
        object sender, 
        ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState.ToString();
    }

আপনি যদি সর্বদা একই ক্ষেত্রটি আপডেট করতে চান তবে তা ঠিক। আপনার যদি আরও জটিল আপডেটগুলি পেতে থাকে তবে আপনি ইউআই রাষ্ট্রের প্রতিনিধিত্ব করতে ক্লাস সংজ্ঞায়িত করতে পারেন এবং এটিকে রিপোর্টপ্রোগ্রেস পদ্ধতিতে পাস করতে পারেন।

একটি চূড়ান্ত জিনিস, WorkerReportsProgressপতাকা সেট করা নিশ্চিত করুন , বা ReportProgressপদ্ধতিটি সম্পূর্ণ উপেক্ষা করা হবে।


2
প্রসেসিং শেষে, ইউজার ইন্টারফেসের মাধ্যমে আপডেট করাও সম্ভব backgroundWorker1_RunWorkerCompleted
ডেভিডআরআর

41

বেশিরভাগ উত্তর ব্যবহার করে Control.Invokeযা একটি দৌড়ের শর্ত যা হওয়ার জন্য অপেক্ষা করছে । উদাহরণস্বরূপ, গৃহীত উত্তরটি বিবেচনা করুন:

string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate { 
    someLabel.Text = newText; // runs on UI thread
});

যদি ব্যবহারকারী কল করার আগে ফর্মটি বন্ধ করে this.Invokeরাখে (মনে রাখবেন, thisএটি হ'ল Formঅবজেক্ট) তবে একটি ObjectDisposedExceptionসম্ভবত বরখাস্ত করা হবে।

সমাধানটি হ'ল SynchronizationContextবিশেষত hamilton.danielbSynchronizationContext.Current হিসাবে যেমন ব্যবহার করা হয় (অন্যান্য উত্তর নির্দিষ্ট প্রয়োগের উপর নির্ভর করে যা সম্পূর্ণ অপ্রয়োজনীয়)। তার পরিবর্তে আমি তার কোডটি ব্যবহার করার পরিবর্তে কিছুটা সংশোধন করব (কারণ সাধারণত শ্রমিক থ্রেডের অপেক্ষা করার দরকার নেই):SynchronizationContextSynchronizationContext.PostSynchronizationContext.Send

public partial class MyForm : Form
{
    private readonly SynchronizationContext _context;
    public MyForm()
    {
        _context = SynchronizationContext.Current
        ...
    }

    private MethodOnOtherThread()
    {
         ...
         _context.Post(status => someLabel.Text = newText,null);
    }
}

নোট 4.0.০ তে এবং আপনার আপাতত অসম্পূর্ণ ক্রিয়াকলাপের জন্য কার্যগুলি ব্যবহার করা উচিত Note সমতুল্য টাস্ক-ভিত্তিক পদ্ধতির জন্য (ব্যবহার করে ) এন-সানের উত্তর দেখুন TaskScheduler.FromCurrentSynchronizationContext

অবশেষে, .NET 4.5 এবং আপ আপনার কাছে ব্যবহার করতে পারেন Progress<T>(যা মূলত যেমনটি SynchronizationContext.Currentতার সৃষ্টি উপরে) হিসাবে দ্বারা প্রদর্শিত Ryszard Dżegan এর ক্ষেত্রে এখনও কাজ যেখানে UI 'তে কোড চালানোর জন্য দীর্ঘক্ষন ধরে চলা অপারেশন প্রয়োজনের জন্য।


37

আপনাকে নিশ্চিত করতে হবে যে আপডেটটি সঠিক থ্রেডে ঘটেছে; ইউআই থ্রেড

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

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

(কোডটি এখানে আমার মাথার বাইরে টাইপ করা হয়েছে, সুতরাং আমি সঠিক বাক্য গঠন ইত্যাদি পরীক্ষা করিনি, তবে এটি আপনাকে চালিত করা উচিত))

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 ( ... );
      }
   }
}

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

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

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

MyEvent.Raise(this, EventArgs.Empty);

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


আসলে, তবে আমি এই বিষয়টি নিয়ে আমার জিইউআই কোডটি 'গণ্ডগোল "করতে পছন্দ করি না। আমার জিইউআইয়ের দিকে নজর দেওয়া দরকার কিনা তা বিবেচনা করা উচিত নয়। অন্য কথায়: আমি মনে করি না যে প্রসঙ্গ-সোয়েথ সম্পাদন করা জিইউআইয়ের দায়বদ্ধতা।
ফ্রেডেরিক ঘেইসেলস

1
ডেলিগেটকে পৃথক করে দেওয়া ইত্যাদিকে অতিরিক্ত চাপ দেওয়া মনে হয় - কেন শুধু নয়: সিঙ্ক্রোনাইজেশন কনটেক্সট.কন্টেন.সেন্ড (প্রতিনিধি {মাইভেন্ট (...);}, নাল);
মার্ক গ্র্যাভেল

আপনার কাছে কি সবসময় সিঙ্ক্রোনাইজেশন কনটেক্সট অ্যাক্সেস থাকে? এমনকি যদি আপনার ক্লাসটি একটি শ্রেণিবদ্ধ হয়?
ফ্রেডেরিক গেইসেলস

29

আপনাকে জিইউআই থ্রেডে পদ্ধতিটি চালু করতে হবে। আপনি কন্ট্রোল কল করে এটি করতে পারেন।

উদাহরণ স্বরূপ:

delegate void UpdateLabelDelegate (string message);

void UpdateLabel (string message)
{
    if (InvokeRequired)
    {
         Invoke (new UpdateLabelDelegate (UpdateLabel), message);
         return;
    }

    MyLabelControl.Text = message;
}

1
ইনভোক লাইনটি আমাকে একটি সংকলক ত্রুটি দেয়। 'System.Windows. Forms.Control.Invoke (System.Delegate, অবজেক্ট []) এর জন্য সেরা ওভারলোডেড পদ্ধতি ম্যাচটির কিছু অবৈধ যুক্তি রয়েছে
ক্রুয়েলআইও

28

দৃশ্যের তুচ্ছতার কারণে আমার কাছে আসলে স্থিতির জন্য ইউআই থ্রেড পোল রাখতে পারি। আমি মনে করি আপনি এটি দেখতে পাবেন যে এটি বেশ মার্জিত হতে পারে।

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খুব ঘন ঘন কল করবেন না বা এটি বার্তা পাম্পকে ছাড়িয়ে যেতে পারে।
  • কলিং Invokeকর্মী থ্রেডে একটি অবরোধ আহবান। এটি সেই থ্রেডে করা কাজ সাময়িকভাবে থামিয়ে দেবে।

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

  • ইউআই এবং কর্মী থ্রেডগুলি দৃ Control.Invokeor Control.BeginInvoke়ভাবে জুড়ে দেয় এমন বা পদ্ধতির বিপরীতে আলগাভাবে মিলিত থাকে led
  • ইউআই থ্রেড কর্মী থ্রেডের অগ্রগতিকে বাধা দেয় না।
  • ইউআই থ্রেড আপডেট করার সময় ব্যয় করতে পারে না কর্মী থ্রেড।
  • ইউআই এবং কর্মী থ্রেডগুলি যে ব্যবধানগুলিতে অপারেশন করে সেগুলি স্বতন্ত্র থাকতে পারে।
  • কর্মী থ্রেড ইউআই থ্রেডের বার্তা পাম্পকে ছাড়িয়ে নিতে পারে না।
  • ইউআই থ্রেডটি কখন এবং কতবার ইউআই আপডেট হয় তা নির্ধারণ করতে পারে to

3
ভাল ধারণা. ওয়ার্কারথ্রেড শেষ হয়ে গেলে আপনি কীভাবে টাইমারটি সঠিকভাবে নিষ্পত্তি করতে পারেন তা কেবলমাত্র আপনি উল্লেখ করেন নি। নোট করুন অ্যাপ্লিকেশন শেষ হওয়ার পরে এটি সমস্যার কারণ হতে পারে (যেমন ব্যবহারকারী অ্যাপ্লিকেশনটি বন্ধ করে দেয়)। কীভাবে এটি সমাধান করবেন আপনার ধারণা আছে?
ম্যাট

@ ম্যাট Elapsedইভেন্টের জন্য কোনও বেনামি হাতল ব্যবহার করার পরিবর্তে , আপনি সদস্য পদ্ধতি ব্যবহার করেন যাতে ফর্মটি নিষ্পত্তি হওয়ার সাথে সাথে আপনি টাইমারটি সরিয়ে ফেলতে পারেন ...
ফিল 1970

@ ফিল 1970 - ভাল কথা। আপনি বোঝাতে চেয়েছিলেন System.Timers.ElapsedEventHandler handler = (s, a) => { MyProgressLabel.Text = m_Text; };এবং এর মাধ্যমে বরাদ্দ দিয়েছিলেন m_Timer.Elapsed += handler;, পরে নিষ্পত্তি প্রসঙ্গে একটি m_Timer.Elapsed -= handler;সঠিক কথা বলছি আমি? এবং এখানে আলোচনা হিসাবে পরামর্শ অনুসরণ নিষ্পত্তি / সমাপ্তির জন্য ।
ম্যাট

27

পূর্বের উত্তরের ইনভোক স্টাফগুলির কোনওটিই প্রয়োজনীয় নয়।

আপনার উইন্ডোজফর্মসিনক্রোনাইজেশন কনটেক্সটটি দেখতে হবে:

// 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
}

4
আপনি কী মনে করেন পোস্ট পদ্ধতিটি হুডের নীচে ব্যবহার করে? :)
অবিশ্বাস্যভাবে

23

এই নেট। ফ্রেমওয়ার্ক 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");

23

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

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

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

  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

21

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

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

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


21

এটি আমার সি # 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. এটি "মেম্বার এক্সপ্রেসনের হিসাবে" ফলাফলটিতে নাল চেকিং যুক্ত করে।
  2. এটি স্থিতিশীল টাইপ-সুরক্ষা উন্নত করে।

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


21
Label lblText; //initialized elsewhere

void AssignLabel(string text)
{
   if (InvokeRequired)
   {
      BeginInvoke((Action<string>)AssignLabel, text);
      return;
   }

   lblText.Text = text;           
}

নোটটি এর BeginInvoke()চেয়ে বেশি পছন্দ করা হয়েছে Invoke()কারণ এটি অচল হয়ে যাওয়ার সম্ভাবনা কম করে (তবে কেবলমাত্র কোনও লেবেলে পাঠ্য নির্ধারণের সময় এটি এখানে কোনও সমস্যা নয়):

ব্যবহার করার সময় Invoke()আপনি পদ্ধতিটি ফিরে আসার অপেক্ষায় রয়েছেন। এখন, এটি এমন হতে পারে যে আপনি চালিত কোডটিতে এমন কিছু করেছিলেন যার জন্য থ্রেডটির জন্য অপেক্ষা করতে হবে, যা আপনি কল করছেন এমন কিছু ফাংশন সমাহিত করা হলে তা তাত্ক্ষণিকভাবে স্পষ্ট নাও হতে পারে, যা ইভেন্ট হ্যান্ডলারের মাধ্যমেই পরোক্ষভাবে ঘটতে পারে। সুতরাং আপনি থ্রেডের জন্য অপেক্ষা করতে থাকবেন, থ্রেডটি আপনার জন্য অপেক্ষা করবে এবং আপনি অচল অবস্থায় রয়েছেন।

এর ফলে আমাদের প্রকাশিত কিছু সফ্টওয়্যার হ্যাং হয়ে যায়। প্রতিস্থাপন দ্বারা ফিক্স করা সহজ যথেষ্ট ছিল Invoke()সঙ্গে BeginInvoke()। যদি আপনার সিঙ্ক্রোনাস অপারেশনের প্রয়োজন না থাকে তবে যদি আপনার কোনও ফেরতের মান প্রয়োজন হয় তবে এটি ব্যবহার করতে পারেন BeginInvoke()


20

আমি যখন একই সমস্যার মুখোমুখি হয়েছি তখন আমি গুগলের কাছ থেকে সহায়তা চেয়েছিলাম, তবে আমাকে একটি সহজ সমাধান দেওয়ার চেয়ে এটি আমাকে 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(() => .....)। আমি যখন কোনও থ্রেডে কাজ করি তখন আমি বেনামে ফাংশন বা ল্যাম্বডা এক্সপ্রেশন ব্যবহার করি। কোডের রেখাগুলি হ্রাস করার জন্য আপনি ThreadStart(..)পদ্ধতিটি খুব বেশি ব্যবহার করতে পারেন যা আমার এখানে ব্যাখ্যা করার কথা নয়।


17

এই জাতীয় কিছু ব্যবহার করুন:

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

যদি আপনার কাছে থাকে e.ProgressPercentage, আপনি যে পদ্ধতিটি কল করছেন সেটি থেকে আপনি কি ইতিমধ্যে ইউআই থ্রেডে নেই?
লার্সটেক

প্রগ্রেস চেঞ্জড ইভেন্টটি ইউআই থ্রেডে চলে। এটি ব্যাকগ্রাউন্ড ওয়ার্কার ব্যবহারের অন্যতম সুবিধা। সম্পূর্ণ ইভেন্টটি গুই-তেও চলে। অ-ইউআই থ্রেডে চলমান একমাত্র জিনিস হ'ল ডো ওয়ার্ক পদ্ধতি।
লার্সটেক

15

আপনি ইতিমধ্যে বিদ্যমান প্রতিনিধি ব্যবহার করতে পারেন Action:

private void UpdateMethod()
{
    if (InvokeRequired)
    {
        Invoke(new Action(UpdateMethod));
    }
}

14

আমার সংস্করণটি একটি লাইন sert োকানো পুনরাবৃত্ত "মন্ত্র" এর জন্য:

কোন যুক্তি ছাড়াই:

    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!
    }

এটি আইটি


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

আপনি যেমন দেখছেন if(InvokeRequired) {something long}ঠিক "এই ফাংশনটি অন্য থ্রেড থেকে কল করা নিরাপদ"।


13

এটি ব্যবহার করে লেবেলটি রিফ্রেশ করার চেষ্টা করুন

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate() { };

    public static void Refresh(this UIElement uiElement)
    {
        uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }
}

এটি কি উইন্ডোজ ফর্মগুলির জন্য ?
কিকিনেট

13

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

SynchronizationContext _context;

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

var _context = SynchronizationContext.Current;

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

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

12

আপনার অবশ্যই অনুরোধ এবং প্রতিনিধি ব্যবহার করা উচিত

private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });

12

অন্যান্য প্রশ্নের বেশিরভাগ উত্তর আমার কাছে এই প্রশ্নটির জন্য কিছুটা জটিল (আমি সি # তে নতুন), তাই আমি আমার লিখছি:

আমার একটি ডাব্লুপিএফ আছে অ্যাপ্লিকেশন রয়েছে এবং একজন শ্রমিককে নীচে হিসাবে সংজ্ঞায়িত করেছি:

সমস্যা:

BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
    // This is my DoWork function.
    // It is given as an anonymous function, instead of a separate DoWork function

    // I need to update a message to textbox (txtLog) from this thread function

    // Want to write below line, to update UI
    txt.Text = "my message"

    // But it fails with:
    //  'System.InvalidOperationException':
    //  "The calling thread cannot access this object because a different thread owns it"
}

সমাধান:

workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
    // The below single line works
    txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}

উপরের লাইনের অর্থ কী তা আমি এখনও খুঁজে পাইনি তবে এটি কার্যকর works

জন্য WinForms :

সমাধান:

txtLog.Invoke((MethodInvoker)delegate
{
    txtLog.Text = "my message";
});

প্রশ্নটি ডাব্লুপিএফ নয়, উইনফর্মের বিষয়ে ছিল।
মার্ক এল।

ধন্যবাদ। উপরে Winforms সমাধান যুক্ত করা হয়েছে।
মনোহর রেড্ডি পোড়্ডে

... যা এই একই প্রশ্নে অবশ্য আরও অনেক উত্তরগুলির একটি অনুলিপি, তবে ঠিক আছে। কেন সমাধানের অংশ না হয়ে কেবল আপনার উত্তরটি মুছবেন?
মার্ক এল।

হুম, ঠিক আছে আপনি, তবেই, আপনি আমার উত্তরটি মনোযোগ সহকারে পড়ুন, প্রথম অংশটি (কারণ আমি উত্তরটি কেন লিখেছিলাম), এবং আশা করি কিছুটা মনোযোগ দিয়ে আপনি দেখতে পাচ্ছেন যে ঠিক সেই একই সমস্যাটি ছিল এবং আজকের জন্য এটি উত্সাহিত হয়েছে আমার সহজ উত্তর, এবং আরও বেশি পরিমাণে যদি আপনি এই সমস্ত ঘটনাটি কেন ঘটেছিল তার আসল কাহিনীটি আগে থেকেই জানতে পারতেন তবে আমি ডাব্লুপিএফ অনুসন্ধান করার পরেও সেই গুগল আমাকে এখানে পাঠায়। নিশ্চিত যেহেতু আপনি এগুলি কম বা কম স্পষ্টত 3 কারণে মিস করেছেন তাই আমি বুঝতে পারি আপনি কেন আপনার ডাউনটোটটি সরিয়ে ফেলবেন না। ঠিক আছেটিকে পরিষ্কার করার পরিবর্তে, এমন একটি নতুন কিছু তৈরি করুন যা আরও বেশি কঠিন।
মনোহর রেড্ডি পোরেড্ডি


8

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

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

সেখানে lblThresholdএকটি লেবেল রয়েছে এবং Speed_Thresholdএটি একটি বৈশ্বিক চলক।


8

আপনি যখন ইউআই থ্রেডে থাকবেন তখন আপনি এটির সিঙ্ক্রোনাইজেশন প্রসঙ্গ টাস্ক শিডিয়ুলারের জন্য জিজ্ঞাসা করতে পারেন। এটি আপনাকে এমন একটি টাস্কশেডুলার দেবে যা ইউআই থ্রেডের সমস্ত কিছুকে শিডিয়ুল করে।

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

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;
  }
}

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


1
কলিংটি Task.Startসাধারণত একটি ভাল অনুশীলন নয় ব্লগস.এমএসএন
ওহাদ স্নাইডার

8

আমি কেবল উত্তরগুলি পড়েছি এবং এটি খুব উত্তপ্ত বিষয় বলে মনে হচ্ছে। আমি বর্তমানে .NET 3.5 এসপি 1 এবং উইন্ডোজ ফর্ম ব্যবহার করছি।

পূর্ববর্তী উত্তরে সুপরিচিত সূত্রটি যে ইনভোকের্কায়ার্ড সম্পত্তি ব্যবহার করে তা বেশিরভাগ ক্ষেত্রেই আবৃত হয় তবে পুরো পুলটি নয়।

হ্যান্ডেলটি এখনও তৈরি না হলে কী হবে ?

InvokeRequired সম্পত্তি, হিসাবে বর্ণনা এখানে (দুটিই MSDN করার Control.InvokeRequired প্রপার্টি রেফারেন্স) ফেরৎ সত্য যদি কল একটি থ্রেড যে গুই থ্রেড নয়, মিথ্যা পারেন যদি কল গুই থ্রেড থেকে তৈরি করা হয়েছিল থেকে তৈরি হয়েছে, অথবা আপনি হাতল ছিল এখনও তৈরি হয়নি।

আপনি কোনও মডেল ফর্মটি অন্য থ্রেড দ্বারা দেখানো এবং আপডেট করতে চাইলে আপনি একটি ব্যতিক্রম জুড়ে আসতে পারেন। যেহেতু আপনি চান যে ফর্মটি Modally দেখানো হয়েছে, আপনি নিম্নলিখিতগুলি করতে পারেন:

private MyForm _gui;

public void StartToDoThings()
{
    _gui = new MyForm();
    Thread thread = new Thread(SomeDelegate);
    thread.Start();
    _gui.ShowDialog();
}

এবং প্রতিনিধি জিইউআইতে একটি লেবেল আপডেট করতে পারে:

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.InvokeRequired)
        _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
    else
        _gui.Label1.Text = "Done!";
}

এটি একটি কারণ হতে পারে InvalidOperationException যদি সময় এটি গুই থ্রেড লাগে তৈরি করতে চেয়ে লেবেল এর আপডেট "কম সময় লাগবে" (এটা পড়তে এবং একটি সরলীকরণ যেমন ব্যাখ্যা) আগের অপারেশন ফরম এর হাতলশো ডায়ালগ () পদ্ধতিতে এটি ঘটে ।

আপনার হ্যান্ডেলের জন্য এটি পরীক্ষা করা উচিত :

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.IsHandleCreated)  //  <---- ADDED
        if(_gui.InvokeRequired)
            _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
        else
            _gui.Label1.Text = "Done!";
}

আপনি সব ব্যবস্থা করতে সক্ষম যদি অপারেশন সম্পাদন করতে হাতল এখনো তৈরি হয় নি: (উপরের কোড দেখানো মত) আপনি শুধু গুই আপডেট উপেক্ষা করতে পারেন অথবা আপনি অপেক্ষা করতে পারেন (আরও ঝুঁকিপূর্ণ)। এটি প্রশ্নের উত্তর দেওয়া উচিত।

Alচ্ছিক স্টাফ: ব্যক্তিগতভাবে আমি নিম্নলিখিত কোডিং করতে এসেছি:

public class ThreadSafeGuiCommand
{
  private const int SLEEPING_STEP = 100;
  private readonly int _totalTimeout;
  private int _timeout;

  public ThreadSafeGuiCommand(int totalTimeout)
  {
    _totalTimeout = totalTimeout;
  }

  public void Execute(Form form, Action guiCommand)
  {
    _timeout = _totalTimeout;
    while (!form.IsHandleCreated)
    {
      if (_timeout <= 0) return;

      Thread.Sleep(SLEEPING_STEP);
      _timeout -= SLEEPING_STEP;
    }

    if (form.InvokeRequired)
      form.Invoke(guiCommand);
    else
      guiCommand();
  }
}

আমি আমার ফর্মগুলি ফিড করি যা এই থ্রেডস্যাফগুইকম্যান্ডের উদাহরণ সহ অন্য থ্রেড দ্বারা আপডেট হয় এবং আমি জিওআই আপডেট করার পদ্ধতিগুলি (আমার ) এর মতো সংজ্ঞা দিই:

public void SetLabeTextTo(string value)
{
  _threadSafeGuiCommand.Execute(this, delegate { Label1.Text = value; });
}

এইভাবে আমি নিশ্চিত যে আমি আমার জিইউআই আপডেট করব যা থ্রেড কলটি করবে, বিকল্পভাবে একটি সুসংজ্ঞায়িত পরিমাণ (সময়সীমা) এর জন্য অপেক্ষা করছে।


1
এটি খুঁজতে এখানে এসেছি, কারণ আমি ইসহ্যান্ডল ক্রিয়েটেডের জন্যও যাচাই করেছি। অন্য একটি সম্পত্তি যাচাই করার জন্য এটি হ'ল ইসডিসপোজড। যদি আপনার ফর্মটি নিষ্পত্তি হয় তবে আপনি এটিতে ইনভোক () কল করতে পারবেন না। আপনার পটভূমি থ্রেডটি সম্পূর্ণ হওয়ার আগে যদি ব্যবহারকারী ফর্মটি বন্ধ করে দেয় তবে আপনি চান না যে ফর্মটি নিষ্পত্তি হওয়ার পরে এটি ইউআইতে ফিরে কল করার চেষ্টা করছে।
জন

আমি বলব যে এটি শুরু করা খারাপ ধারণা ... অথবা আপনি প্রথমে সমস্ত প্রক্রিয়াজাতকরণ করবেন এবং তারপরে ফলাফলটি নতুন ফর্মটিতে তৈরি করবেন pass একই সাথে উভয়টি করার ক্ষেত্রে সাধারণত প্রান্তিক সুবিধা পাওয়া যায় তবে রক্ষণাবেক্ষণের কোড অনেক কম থাকে।
ফিল 1970

বর্ণিত দৃশ্যে ব্যাকগ্রাউন্ড-থ্রেড কাজের অগ্রগতি ভিউ হিসাবে ব্যবহৃত একটি মডেল ফর্ম বিবেচনা করে। কারণ এটি অবশ্যই মডেল হওয়া উচিত, অবশ্যই এটি ফর্ম.শোডায়ালগ () পদ্ধতিতে কল করে দেখানো উচিত । এটি করে আপনি ফর্মটি বন্ধ না হওয়া পর্যন্ত আপনার কোডটি কার্যকর করেন যা কলটি কার্যকর করা হবে prevent সুতরাং, যদি না আপনি প্রদত্ত উদাহরণ থেকে পৃথকভাবে পটভূমি থ্রেড শুরু করতে না পারেন (এবং অবশ্যই, আপনি এটি করতে পারেন) এই ফর্মটি অবশ্যই ব্যাকগ্রাউন্ড থ্রেড শুরু হওয়ার পরে দেখানো উচিত। এই ক্ষেত্রে, আপনাকে হ্যান্ডলটি তৈরি করার জন্য পরীক্ষা করা দরকার। আপনার যদি কোনও মডেল ফর্মের প্রয়োজন না হয় তবে এটি অন্য গল্প।
সুমে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.