বিলম্বিত ফাংশন কল


92

থ্রেড চালানো চালিয়ে যাওয়ার সময় কোনও ফাংশন কলকে বিলম্ব করার মতো সহজ কোনও সহজ পদ্ধতি আছে?

যেমন

public void foo()
{
    // Do stuff!

    // Delayed call to bar() after x number of ms

    // Do more Stuff
}

public void bar()
{
    // Only execute once foo has finished
}

আমি সচেতন যে একটি টাইমার এবং ইভেন্ট হ্যান্ডলার ব্যবহার করে এটি অর্জন করা যেতে পারে, তবে আমি ভাবছিলাম যে এটি অর্জনের জন্য কোনও স্ট্যান্ডার্ড সি # উপায় আছে কিনা?

যদি কেউ কৌতূহলী হন তবে এর প্রয়োজনীয়তার কারণটি হ'ল foo () এবং বার () বিভিন্ন (একক) ক্লাসে রয়েছে যা ব্যতিক্রমী পরিস্থিতিতে আমার একে অপরকে ডাকতে হবে। সমস্যাটি যে এটি আরম্ভের সময় করা হয়েছিল তাই foo এর জন্য বার বার কল করা দরকার যা তৈরি করা হচ্ছে foo ক্লাসের একটি উদাহরণ প্রয়োজন ... সুতরাং foo সম্পূর্ণরূপে তাত্ক্ষণিকভাবে রয়েছে কিনা তা নিশ্চিত করার জন্য বার () এ বিলম্বিত কলটি পড়ছে .. এটি পিছনে পড়া খারাপ ডিজাইনের প্রায় স্মাক!

সম্পাদনা

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


আপনার এটি ঠিক করতে হবে তবে থ্রেডগুলি ব্যবহার করে নয় (বা অন্য কোনও বিষয় হিসাবে অ্যাস অনুশীলন)
ShuggyCoUk

4
অবজেক্ট ইনিশিয়ালাইজেশন সিঙ্ক্রোনাইজ করতে থ্রেড ব্যবহার করা হ'ল আপনার অন্য কোনও উপায় নেওয়া উচিত। অর্কেস্টেটর আরও ভাল বিকল্প বলে মনে হচ্ছে।
পুনরায় কোডিং করুন

4
কিয়ামত! - নকশা সম্পর্কে মন্তব্য করে আপনি একটি দ্বি-পর্যায়ের সূচনা করার জন্য পছন্দ করতে পারেন। ইউনিটি 3 ডি এপিআই থেকে অঙ্কন, রয়েছে Awakeএবং Startপর্যায়ক্রমে রয়েছে। ইন Awakeফেজ, আপনি নিজেকে কনফিগার করুন, এবং এই পর্যায়ে শেষ নাগাদ সমস্ত বস্তু সক্রিয়া করা হয়। সময় Startফেজ বস্তু একে অপরের সাথে যোগাযোগ শুরু করতে পারবেন।
cod3monk3y

4
গৃহীত উত্তরটি পরিবর্তন করা দরকার
ব্রায়ান ওয়েবস্টার

উত্তর:


178

আধুনিক সি # 5/6 কে ধন্যবাদ :)

public void foo()
{
    Task.Delay(1000).ContinueWith(t=> bar());
}

public void bar()
{
    // do stuff
}

15
এই উত্তরটি 2 কারণে দুর্দান্ত। কোডের সরলতা এবং এই সত্য যে ডিলে কোনও থ্রেড তৈরি করে না এবং থ্রেড পুলটি অন্য টাস্ক.রুন বা টাস্ক.স্টার্টনিউয়ের মতো ব্যবহার করে না ... এটি অভ্যন্তরীণভাবে টাইমার।
Zyo 0

একটি শালীন সমাধান।
x4h1d

6
কিছুটা ক্লিনার (আইএমও) সমতুল্য সংস্করণটিও নোট করুন: টাস্ক.ডেলা (টাইমস্প্যান.ফ্রমসেকেন্ডস (1))। চালিয়ে যাওয়া (_ => বার ());
তারান

4
@ জাইও আসলে এটি আলাদা থ্রেড ব্যবহার করে। এটি থেকে কোনও ইউআই উপাদান অ্যাক্সেস করার চেষ্টা করুন এবং এটি একটি ব্যতিক্রম ট্রিগার করবে।
21:58 এ টিউডারটি

@ টিউডারটি - যদি জাইও সঠিক হয় যে এটি ইতিমধ্যে বিদ্যমান থ্রেডে চলছে যা টাইমার ইভেন্টগুলি চালিত করে, তবে তার বক্তব্যটি হ'ল এটি কোনও নতুন থ্রেড তৈরি করার অতিরিক্ত সংস্থান গ্রহণ করে না , বা থ্রেড পুলে সারি করছে না। (আমি জানি না একটি টাইমার তৈরি উল্লেখযোগ্যভাবে পুকুর থ্রেড করার জন্য একটি টাস্ক কিউইং তুলনায় সস্তা -।, যা একটি থ্রেড তৈরি করা নয় যে থ্রেড পুল পুরো পয়েন্ট হচ্ছে)
ToolmakerSteve

96

আমি নিজেই এর মতো কিছু সন্ধান করছি - আমি নিম্নলিখিতটি নিয়ে এসেছি, যদিও এটিতে টাইমার ব্যবহার করা হয়, এটি প্রাথমিক বিলম্বের জন্য এটি কেবল একবার ব্যবহার করে, এবং কোনও Sleepকল প্রয়োজন হয় না ...

public void foo()
{
    System.Threading.Timer timer = null; 
    timer = new System.Threading.Timer((obj) =>
                    {
                        bar();
                        timer.Dispose();
                    }, 
                null, 1000, System.Threading.Timeout.Infinite);
}

public void bar()
{
    // do stuff
}

( কলব্যাকের মধ্যে টাইমার নিষ্পত্তি করার ধারণার জন্য ফ্রেড দেশচেনেসকে ধন্যবাদ )


4
আমি মনে করি কোনও ফাংশন কলকে বিলম্ব করার জন্য এটি সাধারণভাবে সেরা উত্তর। কোনও থ্রেড নেই, কোনও পটভূমি কাজ করছে না, ঘুম নেই। টাইমার খুব দক্ষ এবং মেমরি / সিপিইউ বুদ্ধিমান।
Zyo

4
@ জ্যো, আপনার মন্তব্যের জন্য ধন্যবাদ - হ্যাঁ টাইমারগুলি দক্ষ, এবং এই ধরণের বিলম্ব অনেক পরিস্থিতিতে কার্যকর হয়, বিশেষত যখন আপনার নিয়ন্ত্রণের বাইরে এমন কোনও কিছুতে হস্তক্ষেপ করা - যার বিজ্ঞপ্তি ইভেন্টগুলির কোনও সমর্থন নেই।
dodgy_coder

আপনি কখন টাইমার নিষ্পত্তি করবেন?
দিদিয়ার এ।

4
এখানে একটি পুরানো থ্রেড পুনরুদ্ধার করা, তবে টাইমারটি এর মতো নিষ্পত্তি হতে পারে: পাবলিক স্ট্যাটিক অকার্যকর কল উইথডিলি (অ্যাকশন পদ্ধতি, ইন্টের বিলম্ব) {টাইমার টাইমার = নাল; var সিবি = নতুন টাইমারক্যালব্যাক ((রাজ্য) => {পদ্ধতি (); টাইমরডিস্পস ();}); টাইমার = নতুন টাইমার (সিবি, নাল, বিলম্ব, টাইমআউট.আইনফিনেট); D সম্পাদনা: ভাল দেখে মনে হচ্ছে আমরা মন্তব্যে কোড পোস্ট করতে পারি না ... আপনি যেভাবেই অনুলিপি / পেস্ট করার সময় ভিজ্যুয়াল স্টুডিওর এটি সঠিকভাবে ফর্ম্যাট করা উচিত: পি
ফ্রেড ডেসচেনেস

6
@ ডজি_কোডার ভুল ব্যবহার timerল্যামডা যে প্রতিনিধি বস্তু আবদ্ধ পরার মধ্যে থেকে স্থানীয় পরিবর্তনশীল cbএটি একটি অতি শীঘ্র দোকান (অবসান বাস্তবায়ন বিস্তারিত) যে কারণ হবে সম্মুখের উত্তোলন করায় Timerযেমন বস্তুর জন্য যতদিন জিসি পরিপ্রেক্ষিতে পৌঁছানো হতে TimerCallbackপ্রতিনিধি নিজেই যোগাযোগ করা যাবে । অন্য কথায়, Timerঅবজেক্ট নিশ্চিত হওয়া পর্যন্ত প্রতিনিধি বস্তুর থ্রেড পুল দ্বারা বরকত হয় সংগৃহীত আবর্জনা এমনটি হয় নি।
cdowie

15

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

static class AsyncUtils
{
    static public void DelayCall(int msec, Action fn)
    {
        // Grab the dispatcher from the current executing thread
        Dispatcher d = Dispatcher.CurrentDispatcher;

        // Tasks execute in a thread pool thread
        new Task (() => {
            System.Threading.Thread.Sleep (msec);   // delay

            // use the dispatcher to asynchronously invoke the action 
            // back on the original thread
            d.BeginInvoke (fn);                     
        }).Start ();
    }
}

প্রসঙ্গে, আমি ICommandএটি কোনও ইউআই এলিমেন্টের বাম মাউস বোতামের সাথে আবদ্ধ ডেবিউ করতে ব্যবহার করছি । ব্যবহারকারীরা ডাবল ক্লিক করছেন যা সকল ধরণের সর্বনাশ ঘটাচ্ছে। (আমি জানি আমি Click/ DoubleClickহ্যান্ডলারগুলিও ব্যবহার করতে পারি , তবে আমি এমন একটি সমাধান চাই ICommandযা বোর্ড জুড়ে কাজ করে )।

public void Execute(object parameter)
{
    if (!IsDebouncing) {
        IsDebouncing = true;
        AsyncUtils.DelayCall (DebouncePeriodMsec, () => {
            IsDebouncing = false;
        });

        _execute ();
    }
}

7

মনে হচ্ছে এই উভয় বস্তুর সৃষ্টির নিয়ন্ত্রণ এবং তাদের আন্তঃনির্ভরশীলতাগুলি নিজের শ্রেণীর মধ্যে না গিয়ে বাহ্যিকভাবে নিয়ন্ত্রণ করা দরকার।


+1, এটির মতো শোনাচ্ছে আপনার কোনও ধরণের অর্কেস্ট্রেটার এবং সম্ভবত কোনও কারখানার দরকার আছে
ng5000

5

এটি প্রকৃতপক্ষে একটি খুব খারাপ নকশা, একক সিঙ্গলটন নিজেই খারাপ নকশা করা যাক।

তবে, আপনার যদি সত্যিই কার্যকর করতে বিলম্ব করার প্রয়োজন হয় তবে আপনি যা করতে পারেন তা এখানে:

BackgroundWorker barInvoker = new BackgroundWorker();
barInvoker.DoWork += delegate
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
        bar();
    };
barInvoker.RunWorkerAsync();

এটি অবশ্য bar()পৃথক থ্রেডে প্রার্থনা করবে । যদি আপনাকে bar()আসল থ্রেডে কল করতে হয় তবে bar()আপনাকে RunWorkerCompletedহ্যান্ডলারের কাছে অনুরোধটি স্থানান্তর করতে বা কিছুটা হ্যাকিংয়ের প্রয়োজন হতে পারে SynchronizationContext


3

ঠিক আছে, আমাকে "ডিজাইন" পয়েন্টের সাথে একমত হতে হবে ... তবে আপনি সম্ভবত একজন মনিটর ব্যবহার করতে পারেন যখন অন্যটি গুরুত্বপূর্ণ বিভাগটি পেরিয়ে যায় তখন ...

    public void foo() {
        // Do stuff!

        object syncLock = new object();
        lock (syncLock) {
            // Delayed call to bar() after x number of ms
            ThreadPool.QueueUserWorkItem(delegate {
                lock(syncLock) {
                    bar();
                }
            });

            // Do more Stuff
        } 
        // lock now released, bar can begin            
    }

2
public static class DelayedDelegate
{

    static Timer runDelegates;
    static Dictionary<MethodInvoker, DateTime> delayedDelegates = new Dictionary<MethodInvoker, DateTime>();

    static DelayedDelegate()
    {

        runDelegates = new Timer();
        runDelegates.Interval = 250;
        runDelegates.Tick += RunDelegates;
        runDelegates.Enabled = true;

    }

    public static void Add(MethodInvoker method, int delay)
    {

        delayedDelegates.Add(method, DateTime.Now + TimeSpan.FromSeconds(delay));

    }

    static void RunDelegates(object sender, EventArgs e)
    {

        List<MethodInvoker> removeDelegates = new List<MethodInvoker>();

        foreach (MethodInvoker method in delayedDelegates.Keys)
        {

            if (DateTime.Now >= delayedDelegates[method])
            {
                method();
                removeDelegates.Add(method);
            }

        }

        foreach (MethodInvoker method in removeDelegates)
        {

            delayedDelegates.Remove(method);

        }


    }

}

ব্যবহার:

DelayedDelegate.Add(MyMethod,5);

void MyMethod()
{
     MessageBox.Show("5 Seconds Later!");
}

4
আমি প্রতি 250 মিলিসেকেন্ড চালানোর জন্য টাইমার এড়াতে কিছু যুক্তি যুক্ত করার পরামর্শ দেব। প্রথম: আপনি বিলম্বটি 500 মিলিসেকেন্ডে বাড়িয়ে তুলতে পারেন কারণ আপনার ন্যূনতম অনুমোদিত ব্যবধানটি 1 সেকেন্ড। দ্বিতীয়: আপনি যখন নতুন প্রতিনিধি যুক্ত হবেন তখনই আপনি টাইমারটি শুরু করতে পারেন এবং যখন কোনও প্রতিনিধি না থাকে তখন এটি বন্ধ করে দিতে পারেন। যখন কিছুই করার নেই তখন সিপিইউ চক্র ব্যবহার চালিয়ে যাওয়ার কোনও কারণ নেই। তৃতীয়: আপনি সমস্ত প্রতিনিধি জুড়ে সর্বনিম্ন বিলম্বের জন্য টাইমার ব্যবধান সেট করতে পারেন। সুতরাং এটি তখনই জেগে ওঠে যখন যখন কোনও প্রতিনিধিকে ডাকার দরকার হয় তখন প্রতি 250 মিলি সেকেন্ডে ঘুম থেকে ওঠার পরিবর্তে কিছু করার আছে কিনা তা দেখার জন্য।
পিক মিকেল

মেথডইনভোকার একটি উইন্ডোজ Forফর্মস অবজেক্ট। দয়া করে ওয়েব বিকাশকারীদের জন্য বিকল্প আছে? উদাহরণস্বরূপ: এমন কিছু যা System.Web.UI.WebControls এর সাথে সংঘর্ষে না।
Fandango68

1

আমি যদিও নিখুঁত সমাধানটি হ'ল একটি টাইমার বিলম্বিত ক্রিয়াটি পরিচালনা করবে handle আপনার যখন বিরতি কম হয় তখন এক সেকেন্ডে FxCop পছন্দ করে না। আমার ডেটাগ্রিড কলাম অনুসারে বাছাই শেষ না করা পর্যন্ত আমার ক্রিয়াগুলি বিলম্বিত করা দরকার। আমি একটি শট টাইমার (AutoReset = মিথ্যা) সমাধান হতে পারে, এবং এটি পুরোপুরি কার্যকর। এবং, FxCop আমাকে সতর্কতা দমন করতে দেবে না!


1

এটি নেট।
কনসের পুরানো সংস্করণগুলিতে কাজ করবে : নিজস্ব থ্রেডে কার্যকর হবে

class CancelableDelay
    {
        Thread delayTh;
        Action action;
        int ms;

        public static CancelableDelay StartAfter(int milliseconds, Action action)
        {
            CancelableDelay result = new CancelableDelay() { ms = milliseconds };
            result.action = action;
            result.delayTh = new Thread(result.Delay);
            result.delayTh.Start();
            return result;
        }

        private CancelableDelay() { }

        void Delay()
        {
            try
            {
                Thread.Sleep(ms);
                action.Invoke();
            }
            catch (ThreadAbortException)
            { }
        }

        public void Cancel() => delayTh.Abort();

    }

ব্যবহার:

var job = CancelableDelay.StartAfter(1000, () => { WorkAfter1sec(); });  
job.Cancel(); //to cancel the delayed job

0

টাইমার এবং ইভেন্টগুলি ব্যতীত কোনও ফাংশনে কলটি বিলম্ব করার কোনও আদর্শ উপায় নেই।

এটি কোনও পদ্ধতিতে কলকে বিলম্ব করার জিইউআই বিরোধী প্যাটার্নের মতো শোনাচ্ছে যাতে আপনি নিশ্চিত হয়ে নিতে পারেন যে ফর্মটি ছিটানো শেষ হয়েছে। ভালো বুদ্ধি নই.


0

ডেভিড ও ডোনোগের উত্তরের ভিত্তিতে বিল্ডিং হ'ল বিলম্বিত প্রতিনিধিটির একটি অনুকূলিত সংস্করণ:

using System.Windows.Forms;
using System.Collections.Generic;
using System;

namespace MyTool
{
    public class DelayedDelegate
    {
       static private DelayedDelegate _instance = null;

        private Timer _runDelegates = null;

        private Dictionary<MethodInvoker, DateTime> _delayedDelegates = new Dictionary<MethodInvoker, DateTime>();

        public DelayedDelegate()
        {
        }

        static private DelayedDelegate Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DelayedDelegate();
                }

                return _instance;
            }
        }

        public static void Add(MethodInvoker pMethod, int pDelay)
        {
            Instance.AddNewDelegate(pMethod, pDelay * 1000);
        }

        public static void AddMilliseconds(MethodInvoker pMethod, int pDelay)
        {
            Instance.AddNewDelegate(pMethod, pDelay);
        }

        private void AddNewDelegate(MethodInvoker pMethod, int pDelay)
        {
            if (_runDelegates == null)
            {
                _runDelegates = new Timer();
                _runDelegates.Tick += RunDelegates;
            }
            else
            {
                _runDelegates.Stop();
            }

            _delayedDelegates.Add(pMethod, DateTime.Now + TimeSpan.FromMilliseconds(pDelay));

            StartTimer();
        }

        private void StartTimer()
        {
            if (_delayedDelegates.Count > 0)
            {
                int delay = FindSoonestDelay();
                if (delay == 0)
                {
                    RunDelegates();
                }
                else
                {
                    _runDelegates.Interval = delay;
                    _runDelegates.Start();
                }
            }
        }

        private int FindSoonestDelay()
        {
            int soonest = int.MaxValue;
            TimeSpan remaining;

            foreach (MethodInvoker invoker in _delayedDelegates.Keys)
            {
                remaining = _delayedDelegates[invoker] - DateTime.Now;
                soonest = Math.Max(0, Math.Min(soonest, (int)remaining.TotalMilliseconds));
            }

            return soonest;
        }

        private void RunDelegates(object pSender = null, EventArgs pE = null)
        {
            try
            {
                _runDelegates.Stop();

                List<MethodInvoker> removeDelegates = new List<MethodInvoker>();

                foreach (MethodInvoker method in _delayedDelegates.Keys)
                {
                    if (DateTime.Now >= _delayedDelegates[method])
                    {
                        method();

                        removeDelegates.Add(method);
                    }
                }

                foreach (MethodInvoker method in removeDelegates)
                {
                    _delayedDelegates.Remove(method);
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
                StartTimer();
            }
        }
    }
}

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


0
private static volatile List<System.Threading.Timer> _timers = new List<System.Threading.Timer>();
        private static object lockobj = new object();
        public static void SetTimeout(Action action, int delayInMilliseconds)
        {
            System.Threading.Timer timer = null;
            var cb = new System.Threading.TimerCallback((state) =>
            {
                lock (lockobj)
                    _timers.Remove(timer);
                timer.Dispose();
                action()
            });
            lock (lockobj)
                _timers.Add(timer = new System.Threading.Timer(cb, null, delayInMilliseconds, System.Threading.Timeout.Infinite));
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.