ইউআই থ্রেডে টাস্কের ধারাবাহিকতা


214

প্রাথমিক কাজটি যে থ্রেড থেকে তৈরি করা হয়েছিল তাতে কোনও টাস্কের ধারাবাহিকতা চালিত হওয়া উচিত তা নির্দিষ্ট করার জন্য কি কোনও 'স্ট্যান্ডার্ড' উপায় আছে?

বর্তমানে আমার কাছে নীচের কোডটি রয়েছে - এটি কাজ করছে তবে প্রেরককে ট্র্যাক করে রাখা এবং একটি দ্বিতীয় অ্যাকশন তৈরি করা অপ্রয়োজনীয় ওভারহেডের মতো মনে হচ্ছে।

dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
    DoLongRunningWork();
});

Task UITask= task.ContinueWith(() =>
{
    dispatcher.Invoke(new Action(() =>
    {
        this.TextBlock1.Text = "Complete"; 
    }
});

আপনার উদাহরণের ক্ষেত্রে, আপনি যেমন ব্যবহার করতে পারেন Control.Invoke(Action)TextBlock1.Invokeপরিবর্তেdispatcher.Invoke
কর্নেল প্যানিক

2
ধন্যবাদ @ কলোনেলপ্যানিক, তবে আমি উইনফর্মগুলি নয়, ডাব্লুপিএফ ব্যবহার করেছি (ট্যাগ হিসাবে)।
গ্রেগ স্যানসম

উত্তর:


352

এর সাথে ধারাবাহিকতা কল করুন TaskScheduler.FromCurrentSynchronizationContext():

    Task UITask= task.ContinueWith(() =>
    {
     this.TextBlock1.Text = "Complete"; 
    }, TaskScheduler.FromCurrentSynchronizationContext());

এটি কেবলমাত্র কার্যকর যদি বর্তমান সম্পাদন প্রসঙ্গটি ইউআই থ্রেডে থাকে।


39
এটির বৈধতা কেবলমাত্র যদি বর্তমান সম্পাদন প্রসঙ্গে UI থ্রেড থাকে। আপনি যদি এই কোডটি অন্য কোনও কার্যের ভিতরে রাখেন, তবে আপনি অবৈধ অপসারণ ( ব্যতিক্রম বিভাগটি দেখুন) পান
stukselbax

3
নেট .৪.৪ ইন জোহান লারসনের উত্তরটি ইউআই থ্রেডে কোনও টাস্কের ধারাবাহিকতার জন্য স্ট্যান্ডার্ড উপায় হিসাবে ব্যবহার করা উচিত। শুধু লিখুন: টাস্কের জন্য অপেক্ষা করুন। রুন (ডোলংরানিং ওয়ার্ক); this.TextBlock1.Text = "সম্পূর্ণ"; আরও দেখুন: ব্লগস.এমএসডিএন
মার্সেল ডাব্লু

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

2
@ মার্সেলডাব্লু - awaitএকটি ভাল প্যাটার্ন - তবে আপনি যদি কোনও asyncপ্রসঙ্গে থাকেন (যেমন কোনও পদ্ধতি ঘোষিত হিসাবে async)। যদি তা না হয় তবে এই উত্তরটির মতো কিছু করা এখনও দরকার।
টুলমেকারস্টেভ

33

অ্যাসিঙ্কের সাহায্যে আপনি কেবল এটি করেন:

await Task.Run(() => do some stuff);
// continue doing stuff on the same context as before.
// while it is the default it is nice to be explicit about it with:
await Task.Run(() => do some stuff).ConfigureAwait(true);

যাহোক:

await Task.Run(() => do some stuff).ConfigureAwait(false);
// continue doing stuff on the same thread as the task finished on.

2
falseসংস্করণে মন্তব্যটি আমাকে বিভ্রান্ত করে। আমি ভেবেছিলাম falseএটি অন্য থ্রেডে চালিয়ে যেতে পারে ।
টুলমেকারস্টেভ

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

@ ম্যাক্সবারাক্লাফ - ধন্যবাদ, আমি ভুলটি পড়েছি যে "একই থ্রেড" বোঝানো হয়েছিল। থ্রেড চলমান যা ঘটে [যা কিছু "করণীয়" টাস্কটি সম্পাদন করার জন্য]] তা ব্যবহার করে সর্বাধিক কার্যকারিতা অর্থে থ্রেডগুলির মধ্যে স্যুইচিং এড়ানো , যা এটি আমার জন্য স্পষ্ট করে।
টুলমেকারস্টেভ

1
প্রশ্নটি কোনও asyncপদ্ধতির ভিতরে থাকা (যা প্রয়োজনীয়, তা ব্যবহার করার জন্য await) নির্দিষ্ট করে না । awaitপাওয়া না গেলে উত্তর কী ?
টুলমেকারস্টেভ

22

আপনার যদি কোনও রিটার্ন মান থাকে তবে আপনাকে ইউআইতে প্রেরণ করতে হবে আপনি জেনেরিক সংস্করণটি এর মতো ব্যবহার করতে পারেন:

এটি আমার ক্ষেত্রে একটি এমভিভিএম ভিউমোডেল থেকে আহ্বান করা হচ্ছে।

var updateManifest = Task<ShippingManifest>.Run(() =>
    {
        Thread.Sleep(5000);  // prove it's really working!

        // GenerateManifest calls service and returns 'ShippingManifest' object 
        return GenerateManifest();  
    })

    .ContinueWith(manifest =>
    {
        // MVVM property
        this.ShippingManifest = manifest.Result;

        // or if you are not using MVVM...
        // txtShippingManifest.Text = manifest.Result.ToString();    

        System.Diagnostics.Debug.WriteLine("UI manifest updated - " + DateTime.Now);

    }, TaskScheduler.FromCurrentSynchronizationContext());

জেনারেটম্যানিস্টের একটি টাইপোর আগে আমি = অনুমান করছি।
সেবাস্তিয়ান এফ।

হ্যাঁ - এখন চলে গেছে! ধন্যবাদ.
সাইমন_উইভার

11

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

 Task.Factory.StartNew(() =>
      {
        DoLongRunningWork();
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
              { txt.Text = "Complete"; }));
      });

2
নিম্নচোট নয় কারণ এটি কিছু পরিস্থিতিতে একটি কার্যকর সমাধান; তবে, গৃহীত উত্তরটি আরও ভাল। এটি প্রযুক্তি-অজ্ঞেয়বাদী (বিসিএলের TaskSchedulerঅংশ, Dispatcherএটি নয়) এবং অগ্নি-অ-ভুলে যাওয়া অ্যাসিঙ্ক অপারেশনগুলি (যেমন BeginInvoke) সম্পর্কে উদ্বিগ্ন হওয়ার কারণে কোনও জটিল জটিল শৃঙ্খলা রচনা করতে ব্যবহার করা যেতে পারে ।
কিরিল শ্লেনস্কি

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

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

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

1
... তবুও, [স্বীকৃত উত্তর] সম্পর্কে ডিনের পর্যবেক্ষণের সিঙ্ক প্রসঙ্গে ট্র্যাক রাখা দরকার যদি কোডটি মূল থ্রেডে না থাকে তবে এটি জবাব দেওয়া গুরুত্বপূর্ণ এবং এটি এড়ানো এই উত্তরের একটি সুবিধা।
টুলমেকারস্টেভ

1

গুগলের মাধ্যমে এখানে পেয়েছি কারণ আমি কোনও টাস্কের অভ্যন্তরে থাকার পরে ইউআই থ্রেডে জিনিসগুলি করার একটি ভাল উপায় খুঁজছিলাম। রুন কল - নিম্নলিখিত কোড ব্যবহার করে awaitআপনি আবার ইউআই থ্রেডে ফিরে যেতে পারেন ।

আমি আশা করি এটা কারো সাহায্যে লাগবে.

public static class UI
{
    public static DispatcherAwaiter Thread => new DispatcherAwaiter();
}

public struct DispatcherAwaiter : INotifyCompletion
{
    public bool IsCompleted => Application.Current.Dispatcher.CheckAccess();

    public void OnCompleted(Action continuation) => Application.Current.Dispatcher.Invoke(continuation);

    public void GetResult() { }

    public DispatcherAwaiter GetAwaiter()
    {
        return this;
    }
}

ব্যবহার:

... code which is executed on the background thread...
await UI.Thread;
... code which will be run in the application dispatcher (ui thread) ...

খুব চালাক! বেশ অনিচ্ছুক যদিও। আমি staticক্লাস করার পরামর্শ দিই UI
থিওডর জৌলিয়াস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.