ব্যাকগ্রাউন্ড ওয়ার্কার বাতিল হওয়ার জন্য কীভাবে অপেক্ষা করবেন?


125

আপনার জন্য স্টাফ করে এমন কোনও বস্তুর একটি কাল্পনিক পদ্ধতি বিবেচনা করুন :

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.CancelAsync();

        //todo: Figure out a way to wait for BackgroundWorker to be cancelled.
    }
}

একজন ব্যাকগ্রাউন্ড ওয়ার্কার হয়ে যাওয়ার জন্য কীভাবে অপেক্ষা করতে পারে?


অতীতে লোকেরা চেষ্টা করেছে:

while (_worker.IsBusy)
{
    Sleep(100);
}

তবে এই ডেডলকস , কারণ ইভেন্টটি পরিচালিত IsBusyহওয়ার আগ পর্যন্ত সাফ করা হয় না RunWorkerCompletedএবং অ্যাপ্লিকেশনটি নিষ্ক্রিয় না হওয়া পর্যন্ত ইভেন্টটি পরিচালনা করা যায় না। কর্মী সম্পন্ন না হওয়া পর্যন্ত অ্যাপ্লিকেশন অলস হবে না। (প্লাস, এটি একটি ব্যস্ত লুপ - জঘন্য।

অন্যরা এটিকে বদ্ধ করার পরামর্শ দিয়েছেন:

while (_worker.IsBusy)
{
    Application.DoEvents();
}

এর সাথে সমস্যাটি হ'ল Application.DoEvents()বর্তমানে সারিতে থাকা বার্তাগুলি প্রক্রিয়াজাতকরণের কারণ হয়ে থাকে, যার ফলে পুনরায় প্রবেশের সমস্যা হয় (। নেট পুনরায় প্রবেশকারী নয়)।

আমি ইভেন্ট সিঙ্ক্রোনাইজেশন অবজেক্টগুলির সাথে জড়িত কিছু সমাধান ব্যবহার করার আশা করব, যেখানে কোডটি কোনও ইভেন্টের জন্য অপেক্ষা করে - যা কর্মীর RunWorkerCompletedইভেন্ট হ্যান্ডলারগুলি সেট করে। কিছুটা এইরকম:

Event _workerDoneEvent = new WaitHandle();

public void CancelDoingStuff()
{
    _worker.CancelAsync();
    _workerDoneEvent.WaitOne();
}

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    _workerDoneEvent.SetEvent();
}

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

সুতরাং আপনি কীভাবে একটি ব্যাকগ্রাউন্ড ওয়ার্কার শেষ করার জন্য অপেক্ষা করতে পারেন?


আপডেট মানুষ এই প্রশ্নটি দ্বারা বিভ্রান্ত বলে মনে হচ্ছে। তাদের মনে হয় যে আমি ব্যাকগ্রাউন্ড ওয়ার্কারকে এইভাবে ব্যবহার করব:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);

যে না যে এটা, না আমি কী করছি, এবং যে হয় না এখানে কি জিজ্ঞাসা করা হচ্ছে। যদি এটি হয় তবে পটভূমি কর্মী ব্যবহার করার কোনও মানে হবে না।

উত্তর:


130

যদি আমি আপনার প্রয়োজনীয়তাটি সঠিকভাবে বুঝতে পারি তবে আপনি এটির মতো কিছু করতে পারেন (কোডটি পরীক্ষিত নয়, তবে সাধারণ ধারণাটি দেখায়):

private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);

public Form1()
{
    InitializeComponent();

    worker.DoWork += worker_DoWork;
}

public void Cancel()
{
    worker.CancelAsync();
    _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while(!e.Cancel)
    {
        // do something
    }

    _resetEvent.Set(); // signal that worker is done
}

7
ব্যাকগ্রাউন্ড কর্মী বাতিল করতে যদি দীর্ঘ সময় নেয় তবে এটি ইউআইকে ব্লক করবে (উদাহরণস্বরূপ পুনরায় রঙ করা হবে না)। ইউআইয়ের সাথে মিথস্ক্রিয়া বন্ধ করার জন্য নিম্নলিখিতগুলির একটি ব্যবহার করা ভাল: স্ট্যাকওভারফ্লো
জো

1
চিকিত্সক ঠিক কী আদেশ করেছেন তা +1 করুন ... যদিও আমি @ জো এর সাথে একমত হই যদি বাতিল করার অনুরোধটি যদি এক সেকেন্ডেরও বেশি সময় নিতে পারে।
dotjoe

যখন বাতিলআসেন্স ওয়েটওনের আগে পরিচালনা করা হয় তখন কী ঘটে? বা বাতিল বাটনটি শুধুমাত্র একবারে কাজ করে।
কোডিংবার্ডফিল্ড

6
((BackgroundWorker)sender).CancellationPendingবাতিল ইভেন্টটি পেতে আমাকে পরীক্ষা করা দরকার
Luuk

1
Luuk দ্বারা উল্লিখিত হিসাবে এটি বাতিল সম্পত্তি নয় যা চেক করা উচিত তবে বাতিলকরণের অর্থ প্রদান ending দ্বিতীয়ত, জোয়েল কোহুর্ন যেমন উল্লেখ করেছেন, থ্রেডটি সমাপ্তির জন্য অপেক্ষা করা প্রথম স্থানে থ্রেড ব্যবহারের উদ্দেশ্যকে পরাস্ত করে।
ক্যাপ্টেন সংবেদনশীল

15

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

দ্বিতীয় ত্রুটি এটি _resetEvent.Set() কখনই বলা হবে না যদি শ্রমিক থ্রেড ব্যতিক্রম ছুঁড়ে ফেলে - মূল থ্রেডটি অনির্দিষ্টকালের জন্য অপেক্ষা করে - তবে এই ত্রুটিটি সহজেই চেষ্টা / শেষ অবধি ব্লক করা যায়।

এটি করার একটি উপায় হল একটি মডেল সংলাপ প্রদর্শন করা যাতে একটি টাইমার থাকে যা ব্যাকগ্রাউন্ড কর্মী কাজ শেষ করে (বা আপনার ক্ষেত্রে বাতিল করা শেষ হয়েছে) তা বারবার পরীক্ষা করে। একবার ব্যাকগ্রাউন্ড কর্মী শেষ হয়ে গেলে, মডেল ডায়ালগটি আপনার অ্যাপ্লিকেশনে নিয়ন্ত্রণ ফিরে আসে। ব্যবহারকারী এটি না হওয়া পর্যন্ত ইউআইয়ের সাথে যোগাযোগ করতে পারে না can't

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


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

1
বাতিলআসিঙ্ক পদ্ধতির মানটি নির্দেশ করার জন্য +1, পটভূমি কর্মীর জন্য অপেক্ষা করা শ্লোক ।
ইয়ান বয়ড

10

আপনারা প্রায় সকলেই প্রশ্নটি দ্বারা বিভ্রান্ত, এবং কোনও শ্রমিক কীভাবে ব্যবহৃত হয় তা বুঝতে পারছেন না।

রানওর্কার কমপ্লিট ইভেন্ট হ্যান্ডলারটি বিবেচনা করুন:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;
}

এবং সব ভাল।

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

private void BlowUpRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    StartClaxon();
    SelfDestruct();
}

এবং এমন একটি পরিস্থিতিও রয়েছে যেখানে আমাদের রকেটের অ্যাক্সেস গেটগুলি খুলতে হবে, তবে একটি গণনা করার সময় নয়:

private void OpenAccessGates()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

এবং অবশেষে, আমাদের রকেটটি জ্বালানী তৈরি করতে হবে, তবে এটি একটি গণনা চলাকালীন অনুমোদিত নয়:

private void DrainRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

কোনও কর্মী বাতিল হওয়ার অপেক্ষার ক্ষমতা ছাড়াই আমাদের তিনটি পদ্ধতিই রানওয়ারকার কমপ্লিটড এভেন্টে স্থানান্তরিত করতে হবে:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;

    if (delayedBlowUpRocket)
        BlowUpRocket();
    else if (delayedOpenAccessGates)
        OpenAccessGates();
    else if (delayedDrainRocket)
        DrainRocket();
}

private void BlowUpRocket()
{
    if (worker != null)
    {
        delayedBlowUpRocket = true;
        worker.CancelAsync();
        return;
    }

    StartClaxon();
    SelfDestruct();
}

private void OpenAccessGates()
{
    if (worker != null)
    {
        delayedOpenAccessGates = true;
        worker.CancelAsync();
        return;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

private void DrainRocket()
{
    if (worker != null)
    {
        delayedDrainRocket = true;
        worker.CancelAsync();
        return;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

এখন আমি আমার কোডটি এর মতো লিখতে পারি, তবে আমি ঠিক করব না। আমি পাত্তা দিচ্ছি না, আমি ঠিক নেই।


18
ওয়েটফোর্ড ওয়ার্কারটোফিনিশ পদ্ধতিটি কোথায়? কোন সম্পূর্ণ উত্স কোড?
কিকিনেট

4

আপনি মধ্যে পরীক্ষা করতে পারবেন RunWorkerCompletedEventArgs মধ্যে RunWorkerCompletedEventHandler কি অবস্থা ছিল দেখতে। সাফল্য, বাতিল বা একটি ত্রুটি।

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
    {
        Console.WriteLine("The worker was cancelled.");
    }
}

আপডেট : আপনার কর্মী .CancelAsync () এটি ব্যবহার করে কল করেছেন কিনা তা দেখার জন্য:

if (_worker.CancellationPending)
{
    Console.WriteLine("Cancellation is pending, no need to call CancelAsync again");
}

2
প্রয়োজনটি হ'ল বাতিল করা ডুং স্টাফ () কর্মী শেষ না হওয়া পর্যন্ত ফিরতে পারে না। এটি কীভাবে সম্পন্ন হয়েছে তা পরীক্ষা করা আগ্রহী নয়।
ইয়ান বয়ড

তারপরে আপনাকে একটি ইভেন্ট তৈরি করতে হবে। এটি সুনির্দিষ্টভাবে ব্যাকগ্রাউন্ড ওয়ার্কারের সাথে কিছুই করার পায় নি, আপনাকে কেবল একটি ইভেন্ট বাস্তবায়ন করতে হবে, এটি শুনতে হবে এবং হয়ে গেলে আগুন লাগবে। এবং রান ওয়ার্কার কমপ্লিটডভেন্টহ্যান্ডলারটি হয়ে গেলে এটি হয়। অন্য একটি ঘটনা আগুন।
সেলে নীলসন

4

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

আপনি যদি কিছু অপেক্ষা করতে চান তবে আলাদা থ্রেডিং কনস্ট্রাক্ট ব্যবহার করুন যা ওয়েটহ্যান্ডল সরবরাহ করে।


1
আপনি কি একটি পরামর্শ দিতে পারেন? একটি ব্যাকগ্রাউন্ড কর্মী মনে হয় একমাত্র থ্রেডিং কনস্ট্রাক্ট যা ব্যাকগ্রাউন্ড ওয়ার্কার অবজেক্টটি তৈরি করা থ্রেডে বিজ্ঞপ্তি প্রেরণ করতে পারে।
ইয়ান বয়ড 21

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

3

আপনি কেবল ব্যাকগ্রাউন্ড ওয়ার্কারের সাথে কেন বেঁধে রাখতে পারবেন না un রুন ওয়ার্কার কমপ্লিট ইভেন্ট। এটি একটি কলব্যাক যা "পটভূমির ক্রিয়াকলাপটি শেষ হয়ে গেলে, বাতিল করা হয়েছে বা কোনও ব্যতিক্রম উত্থাপিত হলে ঘটবে" "


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

1

আমি বুঝতে পারি না আপনি কেন ব্যাকগ্রাউন্ড ওয়ার্কার শেষ হওয়ার জন্য অপেক্ষা করতে চান; এটি সত্যিই ক্লাসের জন্য অনুপ্রেরণার ঠিক বিপরীত মত বলে মনে হচ্ছে।

যাইহোক, আপনি কর্মীদের কল দিয়ে প্রতিটি পদ্ধতি শুরু করতে পারেন sআইএসবিসি এবং এটি চালু থাকলে সেগুলি থেকে বেরিয়ে আসতে have


শব্দের খারাপ পছন্দ; আমি এটি শেষ হওয়ার অপেক্ষায় নেই
ইয়ান বয়ড

1

শুধু বলতে চাই যে আমি এখানে এসেছি কারণ লুপে থাকাকালীন যখন আমি একটি অ্যাসিঙ্ক প্রক্রিয়া চালাচ্ছিলাম তখন অপেক্ষা করার জন্য আমার একজন ব্যাকগ্রাউন্ড কর্মী দরকার, আমার ঠিক করা এই সমস্ত জিনিসগুলির চেয়ে সহজ ছিল ^^

foreach(DataRow rw in dt.Rows)
{
     //loop code
     while(!backgroundWorker1.IsBusy)
     {
         backgroundWorker1.RunWorkerAsync();
     }
}

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


0

এইচএম সম্ভবত আমি আপনার প্রশ্নটি সঠিকভাবে পাচ্ছি না।

ব্যাকগ্রাউন্ড ওয়ার্কার ওয়ার্কার কমপ্লিটড ইভেন্টটিকে একবার তার 'ওয়ার্কারমেডথড' (প্যাটার্ন / ফাংশন / সাব যা ব্যাকগ্রাউন্ড ওয়ার্কার.ডো ওয়ার্ক -ইভেন্ট পরিচালনা করে ) সমাপ্ত হয় তাই বিডব্লিউ এখনও চলছে কিনা তা খতিয়ে দেখার দরকার নেই। আপনি যদি আপনার কর্মীকে থামাতে চান তবে আপনার 'কর্মী পদ্ধতিতে' থাকা বাতিল বিচক্ষণ সম্পত্তিটি পরীক্ষা করুন check


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

ওয়ার্ক কমপ্লিট ইভেন্ট এখনও আগুন হবে।
জোয়েল কোহোর্ন

"তবে বাহিরে থাকা ব্যক্তি, যিনি ব্যাকগ্রাউন্ড কর্মীকে বাতিল করার অনুরোধ করেছেন, কীভাবে এটি হওয়ার অপেক্ষা করবেন?"
ইয়ান বয়ড

ওয়ার্ক কমপ্লেটেড ইভেন্টটি চালানোর জন্য অপেক্ষা করে এটি সম্পন্ন হওয়ার জন্য আপনি অপেক্ষা করেন। আপনি যদি আপনার সমস্ত GUI- এ ক্লিক ক্লিক করে ফিট করে রাখতে আটকাতে চান তবে উপরের উত্তরে @ জো প্রস্তাবিত সমাধানগুলির একটি ব্যবহার করতে পারেন (ফর্মটি অক্ষম করুন, বা কোনও মডেল দেখান)। অন্য কথায়, সিস্টেমের অলস লুপটি আপনার জন্য অপেক্ষা করতে দিন; এটি কখন শেষ হবে (ইভেন্টটি চালিয়ে) you
জিওফ

0

কোনও BackgroundWorkerঅবজেক্টের ওয়ার্কফ্লো মূলত আপনাকে RunWorkerCompletedসাধারণ সম্পাদন এবং ব্যবহারকারী বাতিলকরণের ক্ষেত্রে উভয় ক্ষেত্রে ইভেন্টটি পরিচালনা করতে হবে । এই কারণেই রানওয়ার্কার কমপ্লিটড এভেন্টআরগস.ক্যান্সেলড সম্পত্তি বিদ্যমান। মূলত, এটি সঠিকভাবে করার জন্য আপনার বাতিল পদ্ধতিটিকে নিজের মধ্যে একটি অ্যাসিক্রোনাস পদ্ধতি হিসাবে বিবেচনা করা প্রয়োজন।

এখানে একটি উদাহরণ:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    public class AsyncForm : Form
    {
        private Button _startButton;
        private Label _statusLabel;
        private Button _stopButton;
        private MyWorker _worker;

        public AsyncForm()
        {
            var layoutPanel = new TableLayoutPanel();
            layoutPanel.Dock = DockStyle.Fill;
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

            _statusLabel = new Label();
            _statusLabel.Text = "Idle.";
            layoutPanel.Controls.Add(_statusLabel, 0, 0);

            _startButton = new Button();
            _startButton.Text = "Start";
            _startButton.Click += HandleStartButton;
            layoutPanel.Controls.Add(_startButton, 0, 1);

            _stopButton = new Button();
            _stopButton.Enabled = false;
            _stopButton.Text = "Stop";
            _stopButton.Click += HandleStopButton;
            layoutPanel.Controls.Add(_stopButton, 1, 1);

            this.Controls.Add(layoutPanel);
        }

        private void HandleStartButton(object sender, EventArgs e)
        {
            _stopButton.Enabled = true;
            _startButton.Enabled = false;

            _worker = new MyWorker() { WorkerSupportsCancellation = true };
            _worker.RunWorkerCompleted += HandleWorkerCompleted;
            _worker.RunWorkerAsync();

            _statusLabel.Text = "Running...";
        }

        private void HandleStopButton(object sender, EventArgs e)
        {
            _worker.CancelAsync();
            _statusLabel.Text = "Cancelling...";
        }

        private void HandleWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                _statusLabel.Text = "Cancelled!";
            }
            else
            {
                _statusLabel.Text = "Completed.";
            }

            _stopButton.Enabled = false;
            _startButton.Enabled = true;
        }

    }

    public class MyWorker : BackgroundWorker
    {
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            base.OnDoWork(e);

            for (int i = 0; i < 10; i++)
            {
                System.Threading.Thread.Sleep(500);

                if (this.CancellationPending)
                {
                    e.Cancel = true;
                    e.Result = false;
                    return;
                }
            }

            e.Result = true;
        }
    }
}

আপনি যদি সত্যিই সত্যিই না আপনার পদ্ধতি থেকে প্রস্থান করতে চান, আমি একটি মত একটি পতাকা নির্বাণ করার সুপারিশ করছি AutoResetEventএকটি উদ্ভূত উপর BackgroundWorker, তারপর ওভাররাইড OnRunWorkerCompletedপতাকা সেট করতে। যদিও এটি এখনও ধরণের কলডজি; আমি বাতিল ইভেন্টটিকে একটি অ্যাসিক্রোনাস পদ্ধতির মতো চিকিত্সা করার পরামর্শ দেব এবং বর্তমানে RunWorkerCompletedহ্যান্ডলারের যা কিছু করছে তা করার পরামর্শ দেব ।


রান ওয়ার্কার কমপ্লিটে কোড স্থানান্তর করা যেখানে এটির অন্তর্গত তা নয় এবং এটি সুন্দরও নয়।
ইয়ান বয়েড

0

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

এটার মতো কিছু:

class Test : Form
{
    private BackgroundWorker MyWorker = new BackgroundWorker();

    public Test() {
        MyWorker.DoWork += new DoWorkEventHandler(MyWorker_DoWork);
    }

    void MyWorker_DoWork(object sender, DoWorkEventArgs e) {
        for (int i = 0; i < 100; i++) {
            //Do stuff here
            System.Threading.Thread.Sleep((new Random()).Next(0, 1000));  //WARN: Artificial latency here
            if (MyWorker.CancellationPending) { return; } //Bail out if MyWorker is cancelled
        }
    }

    public void CancelWorker() {
        if (MyWorker != null && MyWorker.IsBusy) {
            MyWorker.CancelAsync();
            System.Threading.ThreadStart WaitThread = new System.Threading.ThreadStart(delegate() {
                while (MyWorker.IsBusy) {
                    System.Threading.Thread.Sleep(100);
                }
            });
            WaitThread.BeginInvoke(a => {
                Invoke((MethodInvoker)delegate() { //Invoke your StuffAfterCancellation call back onto the UI thread
                    StuffAfterCancellation();
                });
            }, null);
        } else {
            StuffAfterCancellation();
        }
    }

    private void StuffAfterCancellation() {
        //Things to do after MyWorker is cancelled
    }
}

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


0

আমি জানি এটি সত্যিই দেরী হয়েছে (5 বছর) তবে আপনি যা খুঁজছেন তা হ'ল একটি থ্রেড এবং একটি সিঙ্ক্রোনাইজেশন কনটেক্সট ব্যবহার করা । ফ্রেমওয়ার্কটি স্বয়ংক্রিয়ভাবে যাদুতে না করে বরং আপনাকে "হাত ধরে" ইউআই থ্রেডে মার্শাল ইউআই কল করতে হবে।

এটি আপনাকে এমন একটি থ্রেড ব্যবহার করতে দেয় যা আপনি প্রয়োজনের জন্য অপেক্ষা করতে পারেন।


0
Imports System.Net
Imports System.IO
Imports System.Text

Public Class Form1
   Dim f As New Windows.Forms.Form
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   BackgroundWorker1.WorkerReportsProgress = True
    BackgroundWorker1.RunWorkerAsync()
    Dim l As New Label
    l.Text = "Please Wait"
    f.Controls.Add(l)
    l.Dock = DockStyle.Fill
    f.StartPosition = FormStartPosition.CenterScreen
    f.FormBorderStyle = Windows.Forms.FormBorderStyle.None
    While BackgroundWorker1.IsBusy
        f.ShowDialog()
    End While
End Sub




Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    Dim i As Integer
    For i = 1 To 5
        Threading.Thread.Sleep(5000)
        BackgroundWorker1.ReportProgress((i / 5) * 100)
    Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    Me.Text = e.ProgressPercentage

End Sub

 Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

    f.Close()

End Sub

End Class

তোমার প্রশ্নটি কি? SO তে আপনাকে প্রশ্ন জিজ্ঞাসা করার সময় নির্দিষ্ট হতে হবে
Alma do

@ এটি কোনও প্রশ্নের উত্তর নয়।
কোড এল ღ ver

0

ফ্রেডেরিক কালসেথের এই সমস্যার সমাধান আমি এখনও অবধি খুঁজে পেয়েছি। অন্যান্য সমাধানগুলি ব্যবহার করে Application.DoEvent()যা সমস্যা তৈরি করতে পারে বা সহজভাবে কাজ করে না। আমাকে তার সমাধানটি পুনরায় ব্যবহারযোগ্য শ্রেণিতে ফেলে দেওয়া যাক। যেহেতু BackgroundWorkerসিল করা হয়নি, তাই আমরা এটি থেকে আমাদের ক্লাসটি বের করতে পারি:

public class BackgroundWorkerEx : BackgroundWorker
{
    private AutoResetEvent _resetEvent = new AutoResetEvent(false);
    private bool _resetting, _started;
    private object _lockObject = new object();

    public void CancelSync()
    {
        bool doReset = false;
        lock (_lockObject) {
            if (_started && !_resetting) {
                _resetting = true;
                doReset = true;
            }
        }
        if (doReset) {
            CancelAsync();
            _resetEvent.WaitOne();
            lock (_lockObject) {
                _started = false;
                _resetting = false;
            }
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        lock (_lockObject) {
            _resetting = false;
            _started = true;
            _resetEvent.Reset();
        }
        try {
            base.OnDoWork(e);
        } finally {
            _resetEvent.Set();
        }
    }
}

পতাকা এবং যথাযথ লকিংয়ের সাহায্যে আমরা নিশ্চিত হয়েছি যে _resetEvent.WaitOne()কিছু কাজ শুরু করা হলেই কেবলমাত্র ডাকা হবে, অন্যথায় _resetEvent.Set();কখনও ডাকা হবে না!

_resetEvent.Set();চেষ্টাটি শেষ পর্যন্ত নিশ্চিত করে যে ডেকে আনা হবে, এমনকি যদি আমাদের ডো-ওয়ার্ক-হ্যান্ডলারে কোনও ব্যতিক্রম ঘটে should অন্যথায় কল করার সময় অ্যাপ্লিকেশন চিরতরে স্থির হয়ে যেতে পারেCancelSync !

আমরা এটি এর মতো ব্যবহার করব:

BackgroundWorkerEx _worker;

void StartWork()
{
    StopWork();
    _worker = new BackgroundWorkerEx { 
        WorkerSupportsCancellation = true,
        WorkerReportsProgress = true
    };
    _worker.DoWork += Worker_DoWork;
    _worker.ProgressChanged += Worker_ProgressChanged;
}

void StopWork()
{
    if (_worker != null) {
        _worker.CancelSync(); // Use our new method.
    }
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 1; i <= 20; i++) {
        if (worker.CancellationPending) {
            e.Cancel = true;
            break;
        } else {
            // Simulate a time consuming operation.
            System.Threading.Thread.Sleep(500);
            worker.ReportProgress(5 * i);
        }
    }
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressLabel.Text = e.ProgressPercentage.ToString() + "%";
}

আপনি RunWorkerCompletedএখানে প্রদর্শিত হিসাবে একটি হ্যান্ডলার যোগ করতে পারেন :
     ব্যাকগ্রাউন্ড ওয়ার্কার ক্লাস (মাইক্রোসফ্ট ডকুমেন্টেশন)


0

ফর্মটি বন্ধ করা আমার উন্মুক্ত লগফিল বন্ধ করে দেয়। আমার ব্যাকগ্রাউন্ড কর্মী সেই লগফিলটি লিখেছেন, তাই আমি দিতে পারি নাMainWin_FormClosing() আমার ব্যাকগ্রাউন্ড কর্মী শেষ না হওয়া পর্যন্ত আমি শেষ । আমি যদি আমার পটভূমি কর্মীটির অবসানের অপেক্ষা না করি তবে ব্যতিক্রম ঘটে।

এটা এত কঠিন কেন?

একটি সাধারণ Thread.Sleep(1500)কাজ করে তবে এটি শাটডাউনটি বিলম্ব করে (খুব বেশি দীর্ঘ হলে), বা ব্যতিক্রম ঘটায় (খুব ছোট হলে) if

ব্যাকগ্রাউন্ড কর্মী শেষ হওয়ার পরে ডান শাট ডাউন করতে, কেবল একটি ভেরিয়েবল ব্যবহার করুন। এটি আমার পক্ষে কাজ করছে:

private volatile bool bwRunning = false;

...

private void MainWin_FormClosing(Object sender, FormClosingEventArgs e)
{
    ... // Clean house as-needed.

    bwInstance.CancelAsync();  // Flag background worker to stop.
    while (bwRunning)
        Thread.Sleep(100);  // Wait for background worker to stop.
}  // (The form really gets closed now.)

...

private void bwBody(object sender, DoWorkEventArgs e)
{
    bwRunning = true;

    BackgroundWorker bw = sender as BackgroundWorker;

    ... // Set up (open logfile, etc.)

    for (; ; )  // infinite loop
    {
        ...
        if (bw.CancellationPending) break;
        ...
    } 

    ... // Tear down (close logfile, etc.)

    bwRunning = false;
}  // (bwInstance dies now.)

0

আপনি রানওয়ার্কার কমপ্লিট ইভেন্টের বাইরে পিগি করতে পারেন। এমনকি যদি আপনি ইতিমধ্যে _ কর্মকারের জন্য একটি ইভেন্ট হ্যান্ডলার যুক্ত করেছেন, আপনি অন্য যুক্ত করতে পারেন যাতে তারা যুক্ত হওয়া ক্রমে কার্যকর করবে।

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => 
        {
            // do whatever you want to do when the cancel completes in here!
        });
        _worker.CancelAsync();
    }
}

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

void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (_worker != null)
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => this.Close());
        _worker.CancelAsync();
        e.Cancel = true;
    }
}

0

আমি asyncপদ্ধতিটি ব্যবহার করি এবং awaitকর্মীর কাজ শেষ করার জন্য অপেক্ষা করি:

    public async Task StopAsync()
    {
        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);
    }

এবং DoWorkপদ্ধতিতে:

    public async Task DoWork()
    {
        _isBusy = true;
        while (!_worker.CancellationPending)
        {
            // Do something.
        }
        _isBusy = false;
    }

এছাড়াও আপনি encapsulate পারে whileলুপ DoWorkসঙ্গে try ... catchসেটে _isBusyহয়false ব্যতিক্রম। অথবা, কেবল পরীক্ষা _worker.IsBusyমধ্যে StopAsyncযখন লুপ।

সম্পূর্ণ বাস্তবায়নের উদাহরণ এখানে:

class MyBackgroundWorker
{
    private BackgroundWorker _worker;
    private bool _isBusy;

    public void Start()
    {
        if (_isBusy)
            throw new InvalidOperationException("Cannot start as a background worker is already running.");

        InitialiseWorker();
        _worker.RunWorkerAsync();
    }

    public async Task StopAsync()
    {
        if (!_isBusy)
            throw new InvalidOperationException("Cannot stop as there is no running background worker.");

        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);

        _worker.Dispose();
    }

    private void InitialiseWorker()
    {
        _worker = new BackgroundWorker
        {
            WorkerSupportsCancellation = true
        };
        _worker.DoWork += WorkerDoWork;
    }

    private void WorkerDoWork(object sender, DoWorkEventArgs e)
    {
        _isBusy = true;
        try
        {
            while (!_worker.CancellationPending)
            {
                // Do something.
            }
        }
        catch
        {
            _isBusy = false;
            throw;
        }

        _isBusy = false;
    }
}

কর্মীটিকে থামানোর জন্য এবং এটির শেষ পর্যন্ত অপেক্ষা করার জন্য:

await myBackgroundWorker.StopAsync();

এই পদ্ধতির সমস্যাগুলি হ'ল:

  1. আপনাকে সমস্ত উপায়ে অ্যাসিঙ্ক পদ্ধতি ব্যবহার করতে হবে।
  2. টাস্কের অপেক্ষা করুন eডেলা সঠিক নয়। আমার পিসিতে, টাস্ক.ডেলা (1) আসলে 20 ডলার অপেক্ষা করে।

-2

ওহ মানুষ, এর মধ্যে কিছু হাস্যকর জটিল হয়ে উঠেছে। আপনাকে যা করতে হবে তা হ'ল ডু ওয়ার্ক হ্যান্ডলারের অভ্যন্তরে ব্যাকগ্রাউন্ড ওয়ার্কার.ক্যান্সেলেশন পেন্ডিং সম্পত্তি পরীক্ষা করা। আপনি যে কোনও সময় এটি পরীক্ষা করতে পারেন। একবার এটি মুলতুবি হয়ে গেলে, সেট করুন e.Cancel = পদ্ধতি থেকে সত্য এবং জামিন।

// পদ্ধতি এখানে বেসরকারী শূন্য ওয়ার্কার_ডোওয়ার্ক (অবজেক্ট প্রেরক, ডওওয়ার্কএভেন্টআর্গস ই) {ব্যাকগ্রাউন্ড ওয়ার্কার বিডব্লু = (ব্যাকগ্রাউন্ড ওয়ার্কার হিসাবে প্রেরক);

// do stuff

if(bw.CancellationPending)
{
    e.Cancel = True;
    return;
}

// do other stuff

}


1
এবং এই সমাধানের সাহায্যে যিনি বাতিলআসেন্সকে ফোন করেছিলেন তিনি কীভাবে ব্যাকগ্রাউন্ড কর্মীকে বাতিল হওয়ার জন্য অপেক্ষা করতে বাধ্য হন?
ইয়ান বয়ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.