ইউআই থ্রেড ব্লক আমার উইন্ডো, কিভাবে যে পরিবর্তন?

এখানে আমার কোড:

public void BroadcastTheConnection()
      {
        try
        {
            //............avoid unnecessary codes 
            while (ServerRunning)
            {
                s = myList.AcceptSocket();// blocking the content until client request accomplished 



                displayText.AppendText("\nConnected");
                Thread tcpHandlerThread = new Thread(tcpHandler);
                tcpHandlerThread.Name = "tcpHandler";
                tcpHandlerThread.Start();

             }

         }catch (Exception ex)
            {
                displayText.AppendText("Error----" + Environment.NewLine + ex.StackTrace);
            }
        }

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

এবং এখানে আমার বাটন:

private void bBroadcast_Click(object sender, EventArgs e)
        {
            BroadcastTheConnection();          
        }

আমি লক বিবৃতি ব্যবহার করতে হবে? নাকি প্রতিনিধিরা? কোন ধারনা? তারপর কিভাবে?

1
নেটওয়ার্ক যোগাযোগ উদ্দেশ্যে শুধুমাত্র ডেডিকেটেড <�কোড> থ্রেড তৈরি করার কথা ভাবতে হবে। তারপর যদি আপনি যোগাযোগ থ্রেড থেকে UI থ্রেডে তথ্য স্থানান্তর করতে চান তবে আপনাকে কিছু সিঙ্ক্রোনাইজেশান প্রসঙ্গ ব্যবহার করতে হবে।
যোগ লেখক m.rogalski, উৎস
আপনি একটি থ্রেড মধ্যে broadcasttheconnection করা প্রয়োজন
যোগ লেখক BugFinder, উৎস
তাই তার হোমওয়ার্ক ?? আমি নিশ্চিত আপনি আপনার প্রশ্ন যেমন চিহ্নিত করা উচিত ছিল কিন্তু তিনি আপনার সাথে এই ধারণার আচ্ছাদিত করা হবে
যোগ লেখক BugFinder, উৎস
আপনি সহজ উপায় সমাধানের জন্য BackgroundWorker ব্যবহার করতে পারেন
যোগ লেখক Mustafa Erdem Köşk, উৎস
@ বুগফিন্ডার - যদি আপনি কিছু মনে করেন না তবে দয়া করে আমাকে একটি উদাহরণ দিন ...
যোগ লেখক Hamun Sunga, উৎস

6 উত্তর

সমস্যা হল যে BroadcastTheConnection() UI থ্রেড থেকে নিজেকে বলা হচ্ছে। যেহেতু এতে থাকা অবস্থায় (সার্ভাররাইনিং) {} গঠন করা হয়েছে, তখনই কোডটি আপনার কোডে ServerRunning মিথ্যা হবে।

একই ফিক্সটি বাস্তবায়নের বিভিন্ন উপায় রয়েছে: UI থ্রেডের সার্ভার কোডটি বন্ধ করুন। এদের প্রত্যেকের তাদের ট্রেডঅফ।

  • BroadcastTheConnection() দীর্ঘ চলমান টাস্ক হিসেবে ব্যবহার করুন (প্রস্তাবিত নয়)
  • একটি কোড তৈরি করুন যেখানে BroadcastTheConnection() প্রধান পদ্ধতি।
  • অ্যাসিঙ্ক্রোনাস সকেট কলগুলি ব্যবহার করুন। (একটি দ্রুত উত্তর জন্য খুব জটিল)

দীর্ঘ চলমান কাজ

Task.Factory.StartNew(BroadcastTheConnection,
                      TaskCreationOptions.LongRunning);

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

উত্সর্গীকৃত থ্রেড

Thread connectionThread = new Thread(BroadcastTheConnection)
{
    Name = "BroadcaseTheConnection Thread",
    IsBackground = true
};
connectionThread.Start();

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

সকেট কোড থেকে UI এর সাথে কাজ করে

যে কোনও সময় আপনাকে UI এ কোনওভাবে ইন্টারঅ্যাক্ট করতে হবে, আপনাকে আবার আপনার UI টি থ্রেডে লাগাতে হবে। WinForms এবং wpf একই জিনিস করার সামান্য ভিন্ন উপায় আছে।

পারে WinForms করুন

myControl.BeginInvoke(myControl.Method);//nonblocking

myControl.Invoke(myControl.Method);//blocking

পারে wpf করুন

myControl.Dispatcher.BeginInvoke(myControl.Method);//nonblocking

myControl.Dispatcher.Invoke(myControl.Method);//blocking

সতর্ক থাকুন, সারিতে BeginInvoke এ অনেকগুলি কল UI UI থ্রেডকে ওভারলোড করতে পারে। সারিতে প্রচুর অনুরোধগুলি বন্ধ করার চেয়ে তাদের ব্যাচ করা ভাল।

1
যোগ
টিপিএল বিমূর্তকরণের সময় থ্রেড ব্যবহার করে, পটভূমির ক্রিয়াকলাপগুলির জন্য অ্যাসাইন-অ্যাটাইট ব্যবহার করে না, তবে APM মডেল ব্যবহার করে শুরু/শেষ ইনকোকে ইতিহাসে ফিরে যাওয়া হয়। Async-Await এই ক্ষেত্রে জন্য সবচেয়ে সহজ সমাধান।
যোগ লেখক Mrinal Kamboj, উৎস
UI কোডটি আপনার কোডটি কার্যকর না হওয়া পর্যন্ত আমন্ত্রণ ব্লক করুন, তাই এটি কার্যকরভাবে লকিং প্রক্রিয়া।
যোগ লেখক Berin Loritsch, উৎস
Invoke() এবং BeginInvoke() পদ্ধতিগুলি WinForms এ নিয়ন্ত্রণ শ্রেণীতে সংজ্ঞায়িত করা হয়েছে। সুতরাং কোনও উইন্ডো, বোতাম, প্যানেল, ইত্যাদি ফাংশন থাকবে যার সাথে আপনি UI থ্রেডে কোড আহ্বান করতে পারবেন। এটি একটি অনুরূপ কাহিনী যা wpf কোডের জন্য, তাদের ডিসপ্যাচার ছাড়া যা আপনাকে একই জিনিস করতে দেয়।
যোগ লেখক Berin Loritsch, উৎস
সেটা ঠিক. আপনাকে স্বাগতম.
যোগ লেখক Berin Loritsch, উৎস
শেষ অংশটি দেখুন: আপনার পটভূমি থ্রেড থেকে UI থ্রেড আমন্ত্রণ করতে হবে। আমি WinForms এবং wpf উভয় জন্য উদাহরণ দিয়েছেন।
যোগ লেখক Berin Loritsch, উৎস
@ হ্যামুনসুঙ্গা, ওটা নিয়ে দুঃখিত। আমার প্রধান সুপারিশটি এমন একটি ডেডিকেটেড থ্রেড ব্যবহার করা যা আপনি একটি নাম দিতে পারেন (বিকল্প 2)। যদি আপনি অনিচ্ছুক আচরণ দেখেন তবে আপনার সংযোগগুলিকে নিয়ন্ত্রণ করার জন্য আপনি যদি দুর্ঘটনাক্রমে একাধিক থ্রেড শুরু করেন তবে আপনি সহজে সনাক্ত করতে পারেন।
যোগ লেখক Berin Loritsch, উৎস
@ কফিরগুই, এটি BroadcastTheConnection() কোডটি দীর্ঘ চলমান থাকার সাথে করতে হবে। এছাড়াও, একই সময়ে যখন আপনি একাধিক ঘটতে থাকেন তখন এটি সনাক্ত করা সহজ। তাই আমি এই একটি উত্সর্গীকৃত থ্রেড উকিল হবে।
যোগ লেখক Berin Loritsch, উৎস
@ হ্যামুনসুঙ্গা, আপনার যুক্তি যথেষ্ট জটিল হলে, আপনি কেবল থ্রেড প্রসারিত করতে চান এবং থ্রেডটি শুরু করতে পারেন। স্টার্ট() পদ্ধতি। যাইহোক, আপনি বেনামী পদ্ধতি ঠিক ব্যবহার করতে পারেন।
যোগ লেখক Berin Loritsch, উৎস
যোগ লেখক Berin Loritsch, উৎস
@ কফিরগ, আপনি থ্রেড কাজগুলি শেষ করার আগে কতজন আপনি করতে পারেন? এবং আপনি একই সময়ে চলমান একই টিসিপি কোড একযোগে রান যদি আপনি কিভাবে সনাক্ত করতে পারেন?
যোগ লেখক Berin Loritsch, উৎস
@ বারিনলরিসচ আপনি কি মজা করছেন? আপনি থ্রেড মত ক্লাস প্রসারিত করা উচিত নয় এবং এটি একটি সিল শ্রেণি কারণ এটি করতে পারবেন না।
যোগ লেখক Kfir Guy, উৎস
BroadcastConnection পদ্ধতি সমস্ত সংযোগ পরিচালনা করার জন্য একটি লুপ ব্যবহার করে, তাই এটি শুধুমাত্র একবার চালানো উচিত।
যোগ লেখক Kfir Guy, উৎস
async এবং অপেক্ষা দীর্ঘ দীর্ঘ কাজ কর্ম সঙ্গে ভাল কাজ। এটা ইউআই ব্লক না।
যোগ লেখক Kfir Guy, উৎস
কেন আপনি Async ব্যবহার করবেন না এবং অপেক্ষা? এটা অনেক সহজ। আমার উত্তর চেক করুন।
যোগ লেখক Kfir Guy, উৎস
আমি দুঃখিত, আমি আসতে পারব না, আমার কাছে যথেষ্ট ২0 টি খ্যাতি নেই ... যেটার জন্য দুঃখিত .... @ বারিন লরিৎস
যোগ লেখক Hamun Sunga, উৎস
ডেডিকেটেড থ্রেড ব্যবহার, বেনামী পদ্ধতি অধিকার?
যোগ লেখক Hamun Sunga, উৎস
আপনাকে অনেক ধন্যবাদ
যোগ লেখক Hamun Sunga, উৎস
আপনাকে অনেক ধন্যবাদ ...: আমি প্রশংসা করছি ... :)
যোগ লেখক Hamun Sunga, উৎস

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

while (ServerRunning)
{
    AsyncHandler handler = delegate(asyncResult)
    {
        //Get the new socket
        Socket socket = myList.EndAcceptSocket(asyncResult);

        //Marshal UI specific code back to the UI thread
        MethodInvoker invoker = delegate()
        {
            listOFClientsSocks.Add(socket);
            listBox1.DataSource = listOFClientsSocks;
            displayText.AppendText("\nConnected");
        };
        listBox1.Invoke(invoker);

        //Call the handler
        tcpHandler();
    }
    IAsyncResult waitResult = myList.BeginAcceptSocket(handler, null);

    //Wait until the async result's wait handle receives a signal
    //Use a timeout to referesh the application every 100 milliseconds
    while (!waitResult.AsyncWaitHandle.WaitOne(100))
    {
        Application.DoEvents();
        if (!ServerRunning)
        {
            break;
        }
    }
}

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

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

1
যোগ

রিমোট শেষের সাথে শুনতে এবং যোগাযোগ করতে থ্রেড ব্যবহার করে সহজ উদাহরণ:

public class ListenerThread
{
   //clients list/queue
    Queue m_Clients;
   //thread used to listen for new connections
    Thread m_Thread;
    Socket m_Socket;
    IPEndPoint m_LocalEndPoint;

    volatile bool m_IsListening;

    public ListenerThread(int port)
    {
       //get this machine hostname
        IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());  
       //resolve ip address from hostname
        IPAddress ipAddress = ipHostInfo.AddressList[0];  
       //create local end point object 
        m_LocalEndPoint = new IPEndPoint(ipAddress, port);  
    }

    void Listen()
    {
       //reset clients list
        m_Clients = new Queue();
       //initialize socket
        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); 
       //bind this socket to listen for incomming connections to specified end point
        m_Socekt.Bind(localEndPoint);
       //start listening with backlog of 1337 connections
        m_Socket.Listen(1337);  
       //dont forget to dispose after socket was used to "unbind" it
        using ( m_Socket )
        {
            while ( m_IsListening )
            {
               //while listening just accept connections and start them at another thread
                Socket client = m_Socket.Accept();
                if ( client != null )
                {
                    m_Clients.Enqueue(new ClientConnection(client));
                }
            }
        }
    }

   //method used to start the listening server
    public void Start()
    {
        if ( m_Thread == null )
        {
            m_Thread = new Thread(Listen);
        }

        m_IsListening = true;
        m_Thread.Start();
    }

   //method used to stop listening server
    public void Stop()
    {
        m_Listening = false;
        m_Thread.Join();
        while ( m_Clients.Count != 0 )
        {
            m_Clients.Dequeue().Kill();
        }
    }
}

// class used to communicate with the client
public class ClientConnection
{
    Socket m_Socket;//client socket
    Thread m_Thread;//communication thread

    volatile bool m_IsCommunicating;

   //this should start immediately because of the incomming connection priority
    internal ClientConnection(Socket socket)
    {
        m_Socket = socket;
        m_Thread = new Thread(Communicate);
        m_Thread.Start();
    }

   //loop in which you should send/receive data
    void Communicate()
    {
        while ( m_IsCommunicating )
        {
           //.. do your communication stuff
        }
    }

   //should be only used by ListenerThread to end communication.
    internal void Kill()
    {
        m_IsCommunicating = false;
        try
        {
            m_Thread.Join(5 * 1000);
            m_Thread.Abort();
        }
        catch(Exception ex) { /*...*/ }
    }
}

এটি সত্যিই সহজ উদাহরণ, তাই আপনার প্রয়োজনীয়তার জন্য এটি সংশোধন করা উচিত।
আপনার উদাহরণের সাথে এটি ব্যবহার করতে শুধুমাত্র শ্রোতা শ্রদ্ধা শুরু করুন:

ListenerThread listener = new ListenerThread(8001);
listener.Start();
displayText.AppendText("The server is running at port 8001...\n");

সর্বশেষ জিনিসটি যদি আপনি UI এ কল করতে চান তবে আমি সিঙ্ক্রোনাইজেশন কনটেক্সট ব্যবহার করে পরামর্শ দেব। ListenerThread কন্সট্রকটারে এটি পরিষ্কার করতে এটি কল করুন:

m_Sync = SynchronizationContext.Current;

এবং অন্য ক্ষেত্র তৈরি করুন:

SynchronizationContext m_Sync;

তারপরে এই কোডটিকে ক্লায়েন্টকোনেশন কন্সট্রকটারে নতুন ক্লায়েন্টকোনেশন (m_Sync, ক্লায়েন্ট) হিসাবে প্রেরণ করুন;

এখন আপনি SynchronizationContext.Post পদ্ধতি যেমন ব্যবহার করতে পারেন। :

m_Sync.Post( state => { someUITextElement.AppendText((string)state); }, "hello world");
1
যোগ

Async ব্যবহার করে পরিবর্তন করুন এবং অপেক্ষা করুন

private async void bBroadcast_Click(object sender, EventArgs e) //-- Async
{
    ipAdrsNew = ipBox.Text;
    portNo = Convert.ToInt32(portBox.Text);
    await BroadcastTheConnection();           //-- await
}

public Task BroadcastTheConnection()
{
   return Task.Run(() =>
   {
       //---- code for BroadcastTheConnection 
   });
}
1
যোগ
সুতরাং নিয়োগ ভুল। সি # তে, অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং করার সঠিক উপায়টি অ্যাসিনক এবং অপেক্ষা করছে। থ্রেড বা কাজের ব্যবহার করা থেকে কতটা সহজ তা দেখতে আমার উত্তরটি পরীক্ষা করে দেখুন।
যোগ লেখক Kfir Guy, উৎস
কেন আপনি সরাসরি কাজ পরিচালনা করবে? শুধু Async ব্যবহার করুন এবং অপেক্ষা করুন।
যোগ লেখক Kfir Guy, উৎস

আপনি C# এ অ্যাসিঙ্ক্রোনাস নেটওয়ার্কিং অর্জন করতে async এবং অপেক্ষা করতে পারেন।

এটি ব্যবহার করে দেখুন (আমি আপনার কোড পুনঃবিনিয়োগ করেছি):

public async Task BroadcastConnectionAsync(IPAddress address, int port)
{
    try
    {
        var listener = new TcpListener(address, port);

        ServerRunning = true;
       //Start Listeneting at the specified port

        listener.Start();
        displayText.AppendText("The server is running at port 8001...\n");

        while (ServerRunning)
        {
            using (var socket = await listener.AcceptSocketAsync())
            {
                listOFClientsSocks.Add(socket);
                listBox1.DataSource = listOFClientsSocks;

                displayText.AppendText("\nConnected");
                new Thread(tcpHandler)
                {
                    Name = "tcpHandler"
                }.Start();
            }
        }
    }
    catch (Exception ex)
    {
        displayText.AppendText("Error----" + Environment.NewLine + ex.StackTrace);
    }
}

এবং আপনার ক্লিক ইভেন্ট হ্যান্ডলার:

private async void bBroadcast_Click(object sender, EventArgs e)
{
    var address = IPAddress.Parse(ipBox.Text);
    int port = Convert.ToInt32(portBox.Text);
    await BroadcastConnectionAsync(address, port);
}
1
যোগ
Async-Await সবচেয়ে ভাল এবং প্রস্তাবিত বিকল্প, যদিও কোনও সমস্যা রয়েছে, ঘটনাটি অকার্যকর রিটার্ন টাইপ হিসাবে, অ্যাসাইন পদ্ধতিটিও কার্যত প্রত্যাবর্তন করা হবে না। এছাড়াও অন্য থ্রেড শুরু করা, Async পদ্ধতির ভিতরে উই নিয়ন্ত্রণ আপডেট করা ভাল ক্রিয়াকলাপ নয়
যোগ লেখক Mrinal Kamboj, উৎস
ধন্যবাদ, আমি এই চেষ্টা করবে ... :)
যোগ লেখক Hamun Sunga, উৎস

আপনি ব্যাকগ্রাউন্ড কর্মী ব্যবহার করতে পারেন। নীচে একই একটি ছোট উদাহরণ।

private void MainMethod()
        {
            BackgroundWorker bg = new BackgroundWorker();
            bg.DoWork += Bg_DoWork;
            bg.RunWorkerCompleted += Bg_RunWorkerCompleted;

        }

        private void Bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //here do your UI related work like changing the color of label or filling up data in grid etc
        }

        private void Bg_DoWork(object sender, DoWorkEventArgs e)
        {
            //Here do your time consuming work without blocking ui thread
        }
0
যোগ