CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 9 of 9

Hybrid View

  1. #1
    Join Date
    Aug 2011
    Posts
    4

    Question C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Hi all!

    This is my very first post so dont hate me if i forget to include something.
    Currently i have build a bot in C#. Nothing Fancy, it connects and i can interact with the stuff on the channel.
    But now i would like to be able to insert commands in the commandline after connecting to the server.
    Ive succesfully seperated the code from the main class to a different class, which will be used in another thread. I create this thread in the main class/thread.

    So here is the deal:
    - Before actually connecting, i would like to send a message to the main thread so it can run other code making my console to be able to recieve user input.
    - Upon closing, i would like to send a terminate signal to the other thread.

    Now im a 'oke' C# guy but im still have a long way to go. This is my very first attempt on multithreading. I cannot find any tutorials on this subject that i can understand so hopefully some of you guru's are able to.

    If added some example code below. Please advice
    Many thanks in advance!

    code:



    Oke this is my main class:

    Code:
    namespace Lydic
    {
    	class LydicBot
    	{   
            private static void Main()
    	    {
                Thread ircConnectionThread = new Thread(GetIrcConnectionMethod());
                ircConnectionThread.Start();
    			// at this point i'd like to recieve the signal from the above thread to continue the following code:
    			executeCommand();
    	    }
            private static ThreadStart GetIrcConnectionMethod()
            {
                return ircConnection;
            }
            static void ircConnection()
            {
                connectionHandler Lydic = new connectionHandler();
            }
    		void executeCommand (string command)
    		{
    			if(String.IsNullOrEmpty(command))
    			{
    				Console.WriteLine("Input command:"); // 
    				string command = Console.ReadLine().ToString();
    				executeCommand(command);
    			} else {
    				// switch case stuff with commands
    			}
    		}
    	}
    }
    Ive created a second class called connectionHandler, wich basically includes the following:

    Code:
    namespace Lydic
    {
    	class connectionHandler
    	{
            private IrcClient irc = new IrcClient();
            private string server = "irc.geekshed.net";
            private int port = 6667;
            private string channel = "#lydic";
    
            private static void LydicBot()
            {
                connectionHandler demo = new connectionHandler();
            }
    
            public connectionHandler()
            {
                Console.WriteLine("Input server address (default: irc.warez-bb.org):");
                string serverAddress = Console.ReadLine();
    
                Console.WriteLine("Input server port (default: 6667):");
                string serverPort = Console.ReadLine();
    
                if (!string.IsNullOrEmpty(serverAddress))
                {
                    server = serverAddress;
                }
    
                if (!string.IsNullOrEmpty(serverPort))
                { //int serverPort = Convert.ToInt32(Console.ReadLine()); 
                    port = Convert.ToInt32(serverPort);
                }
                irc.OnConnected += new EventHandler(OnConnected);
    
                connectToServer();
            }
            public void connectToServer()
            {
                try
                {
                    irc.Connect(server, port);
                }
                catch (Exception e)
                {
                    Console.Write("[{1}] Failed to connect: {0}", e.Message, DateTime.Now.ToShortTimeString());
                    Console.ReadKey();
                }
            }
    
            void OnConnected(object sender, EventArgs e)
            {
                if (server == "irc.warez-bb.org")
                {
                    irc.SendMessage(SendType.Message, "NickServ", "identify DERP");
                }
                irc.Login("Lydic", "Lydic", 0, "Lydic");
                irc.RfcJoin(channel);
    	    // At this point i'd like to send a signal to my main thread to tell him that he can execute the other code, but the code below must be executed as well!
                irc.Listen();
            }
    	}
    }
    Last edited by MarcoDiMarco; August 19th, 2011 at 06:29 PM. Reason: faulty code example

  2. #2
    Join Date
    Aug 2011
    Posts
    4

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    wow i actually posted the wrong code....
    Isnt that the best first mistake i could make?

    Anyways, thats the situation that doesnt work, after firing Listen() it doesnt fire anything else. So in front of that line i would like to signal the other thread to perfom the executeCommand();

    I hope im being clear, if not ill try to explain better.

  3. #3
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Did you edit your post to include the correct code?

  4. #4
    Join Date
    Aug 2011
    Posts
    4

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Quote Originally Posted by Arjay View Post
    Did you edit your post to include the correct code?
    yes i have now. I hope you understand?

  5. #5
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Since you are a noob to multi-threading, I would start by creating a simple test project with 2 threads and use one thread to notify the second thread of an action.

    Code:
    using System;
    using System.Threading;
    
    namespace CG.ThreadNotify
    {
        class Notify
        {
            /// <summary>
            /// Thread safe State - state values that get passed to worker thread
            /// The State property is accessed by 2 different threads for read and write.
            /// As such, it is considered shared data and its access must be synchronized.
            /// Interlocked.Exchange is used for synchronization between threads.
            /// P.S. I know about the atomic nature of Int32 in .Net
            /// </summary>
            public NotifyState State
            {
                get
                {
                    return (NotifyState) Interlocked.Exchange( ref _state, _state );
                }
                set
                {
                    Interlocked.Exchange( ref _state, (int)value );
                }
            }
    
    
            /// <summary>
            /// States to pass to worker thread
            /// </summary>
            public enum NotifyState
            {
                /// <summary>
                /// State 1
                /// </summary>
                S1,
                /// <summary>
                /// State 2
                /// </summary>
                S2, 
                /// <summary>
                /// State 3
                /// </summary>
                S3,
                /// <summary>
                /// X (causes thread to exit)
                /// </summary>
                X
            }
    
            /// <summary>
            /// Prompts user for input.  Possible values are S1, S2, S3, and X (to exit).
            /// Signals worker thread that state has changed. Causes worker thread to display
            /// changed state and/or exit.
            /// </summary>
            public void PromptUser( )
            {
                var keepRunning = true;
    
                do
                {
                    try
                    {
                        switch ( ( NotifyState ) Enum.Parse( typeof( NotifyState )
                            , Console.ReadLine( ).ToUpper( ) ) )
                        {
                        case NotifyState.S1: State = NotifyState.S1; break;
                        case NotifyState.S2: State = NotifyState.S2; break;
                        case NotifyState.S3: State = NotifyState.S3; break;
                        case NotifyState.X:
                            State = NotifyState.X;
                            keepRunning = false;
                            break;
                        }
    
                        // Notify the thread that a change in state has taken place
                        // Causes worker thread to 'wake up' and display state change
                        _notifyEvent.Set( );
                    }
                    catch (Exception)
                    {
                        Console.WriteLine( "Invalid input. Please enter S1, S2, S3 or X (to exit)");
                    }
                } while ( keepRunning );
            }
    
            public void Start( )
            {
                _notifyThread = new Thread( ThreadProc ) {IsBackground = true};
                _notifyThread.Start();
    
                Console.WriteLine( "Thread starting.\n\nPlease enter S1, S2, S3, or X (to exit).\n" );
            }
        
            public void Stop( )
            {
                if (_notifyThread == null) return;
    
                // Set the stop event (causes the thread to exit
                _stopEvent.Set( );
    
                // Wait for the thread to exit
                _notifyThread.Join( 5000 );
    
                // Set the thread object to null, so we create a new one in Start( )
                _notifyThread = null;
    
                Console.WriteLine( "\nThread stopped." );
            }
    
            /// <summary>
            /// Worker thread.  Waits for Notify or Stop events.  Displays change in thread state
            /// and/or exits.
            /// </summary>
            public void ThreadProc( )
            {
                while (true)
                {
                    switch (WaitHandle.WaitAny(new WaitHandle[] {_stopEvent, _notifyEvent}))
                    {
                    case StopEventIndex: // Stop event has been set - exit thread
                        Console.WriteLine( "\n\tThread exiting: State = {0}", State );
                        return;
                    case NotifyEventIndex:  // Notify event has been set - display state change
                        Console.WriteLine( "\tThread notified: State = {0}", State );
                        break;
                    }
                }
            }
    
            private Thread _notifyThread;
            private readonly AutoResetEvent _notifyEvent = new AutoResetEvent( false );
            private readonly AutoResetEvent _stopEvent = new AutoResetEvent( false );
    
            private int _state = (int)NotifyState.S1;
            private const int StopEventIndex = 0;
            private const int NotifyEventIndex = 1;
        }
    
        class Program
        {
            static void Main( )
            {
                var notify = new Notify( );
    
                notify.Start();
    
                notify.PromptUser( );
    
                notify.Stop( );
    
                // Pause slightly before exiting (real programs don't need to do this)
                Thread.Sleep( 3000 );
            }
        }
    }
    Sample details.

    1) AutoResetEvents are used by the main thread to signal state change or exit state in worker thread. Notice that I use events to interact with the worker thread (rather than some other variable).

    2) The State property that gets passed between threads is considered shared data and must be made thread safe. Technically Int32 is changed atomically by the framework, but I want to explicitly synchronize access using Interlocked.Exchange for this sample. To me, it's easier to follow synchronization patterns rather than remember when something is atomic across different platforms.

    3) Notice that the PromptUser method runs in a loop, takes user input, converts that to a NotifyState enum, sets the State property and then calls NotifyEvent.Set( ) to signal the worker thread that a change in state has occurred. Even though this methods sets the NotifyEvent event and State propery, it really knows nothing of threading (as it should be).

    4) Notice the worker thread spends all of its time sleeping (via the WaitAny method)? This is how worker threads should be set up rather than constantly polling (and burning up cpu).

    5) Setting Thread.IsBackground = true. For worker threads, always set this property to true. If you are writing a GUI app and you don't set this, your app may stick around after you close the UI. If you set this property to true in a GUI app, the framework will automatically close worker threads for you.

    6) Notice that the only shared data between the main thread and the worker thread is the State property (which is thread safe). When writing multithreaded programs, it is important to identify shared data and understand when thread synchronization is required. Too often folks run into problems because they don't understand that more than one thread is accessing a piece of data, and therefore don't properly synchronize access to the data.
    Last edited by Arjay; August 23rd, 2011 at 02:15 PM.

  6. #6
    Join Date
    Aug 2011
    Posts
    4

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Hello Arjay,

    I will use your example and explanation. Thank you so much for your time and effort on this.

    Kind regards,
    Mdm.

  7. #7
    Join Date
    Jul 2014
    Posts
    2

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Hello Arjay,

    Thank you for the great example.
    I have copied your code and tried to modify it as a Windows Forms Application -- but I am having trouble converting it
    I know this is an old post ... but Can you help me?

    I am having trouble with the PromptUser()
    I was using a ComboBox to change the State value, but the Form GUI does not allow me to change the value
    Can you help me converting it over to a Windows Forms Application?

    Thanks

  8. #8
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Quote Originally Posted by csharpnoobie View Post
    Hello Arjay,

    Thank you for the great example.
    I have copied your code and tried to modify it as a Windows Forms Application -- but I am having trouble converting it
    I know this is an old post ... but Can you help me?

    I am having trouble with the PromptUser()
    I was using a ComboBox to change the State value, but the Form GUI does not allow me to change the value
    Can you help me converting it over to a Windows Forms Application?

    Thanks
    Create a Windows Form Application project and attempt to incorporate the threading code from above.

    When you get stuck, zip up the complete solution and post it here.

  9. #9
    Join Date
    Jul 2014
    Posts
    2

    Question Re: C# 3.5 Visual Studio 2010: MultiThreading - Main Thread continue on signal

    Can someone do some peer review of my code changes?

    I took the Console app (from Arjay's post above) and made it into a Windows Forms app
    I added a ComboBox to allow the user to change the State
    And a ListBox to show messages

    I removed the do-while loop in PromptUser()
    Now, each time the user change/select the ComboBox, Start() and PromptUser() gets called
    I was trying to keep the same effect of the do-while loop from the original PromptUser() -- the original version kept locking up the Main UI thread

    Was this the ideal approach how to keep my Main UI thread from locking up my Form1???
    Or should I have used some other method

    Can someone advise please?
    Thanks


    class Form1
    Code:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ThreadNotify
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            Notify notify = new Notify();
    
            public string myState;
    
            public void WriteLog(string str)
            {
                if (lstLog.InvokeRequired)
                {
                    lstLog.Invoke(new MethodInvoker(delegate { WriteLog(str); }));
                }
                else
                {
                    lstLog.BeginUpdate();
                    lstLog.Items.Add(str);
                    lstLog.TopIndex = lstLog.Items.Count - 1;
                    lstLog.EndUpdate();
                }
            }
    
            private void cboState_SelectedIndexChanged(object sender, EventArgs e)
            {
                myState = cboState.Text;
                notify.Start();
                notify.PromptUser();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                notify.f1 = this;
            }
        }
    }
    class ThreadNotify
    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    /// <summary>
    /// http://forums.codeguru.com/showthread.php?515576-C-3.5-Visual-Studio-2010-MultiThreading-Main-Thread-continue-on-signal
    /// </summary>
    
    namespace ThreadNotify
    {
        class Notify
        {
            /// <summary>
            /// Thread safe State - state values that get passed to worker thread
            /// The State property is accessed by 2 different threads for read and write.
            /// As such, it is considered shared data and its access must be synchronized.
            /// Interlocked.Exchange is used for synchronization between threads.
            /// P.S. I know about the atomic nature of Int32 in .Net
            /// </summary>
            public NotifyState State
            {
                get
                {
                    return (NotifyState)Interlocked.Exchange(ref _state, _state);
                }
                set
                {
                    Interlocked.Exchange(ref _state, (int)value);
                }
            }
    
            /// <summary>
            /// States to pass to worker thread
            /// </summary>
            public enum NotifyState
            {
                /// <summary>
                /// State 1
                /// </summary>
                S1,
                /// <summary>
                /// State 2
                /// </summary>
                S2,
                /// <summary>
                /// State 3
                /// </summary>
                S3,
                /// <summary>
                /// X (causes thread to exit)
                /// </summary>
                X
            }
    
            /// <summary>
            /// Prompts user for input.  Possible values are S1, S2, S3, and X (to exit).
            /// Signals worker thread that state has changed. Causes worker thread to display
            /// changed state and/or exit.
            /// </summary>
            public void PromptUser()
            {
                if (_notifyThread == null) return;
    
                try
                {
                    //switch ((NotifyState)Enum.Parse(typeof(NotifyState), Console.ReadLine().ToUpper()))
                    switch ((NotifyState)Enum.Parse(typeof(NotifyState), f1.myState))
                    {
                        case NotifyState.S1:
                            State = NotifyState.S1;
                            break;
                        case NotifyState.S2:
                            State = NotifyState.S2;
                            break;
                        case NotifyState.S3:
                            State = NotifyState.S3;
                            break;
                        case NotifyState.X:
                            State = NotifyState.X;
                            break;
                    }
    
                    // Notify the thread that a change in state has taken place
                    // Causes worker thread to 'wake up' and display state change
                    _notifyEvent.Set();
                }
                catch (Exception)
                {
                    f1.WriteLog("Invalid input.  Please enter S1, S2, S3, or X (to exit).");
                }
            }
    
            public void DoSomething()
            {
                string curState = f1.myState;
    
                for (int i = 0; i < 10; i++)
                {
                    if (curState != f1.myState)
                        return;
    
                    f1.WriteLog(State.ToString() + " ... " + i.ToString());
                    Thread.Sleep(1000);
                }
            }
    
            public void Start()
            {
                if (_notifyThread == null)
                {
                    _notifyThread = new Thread(ThreadProc) { IsBackground = true };
                    _notifyThread.Start();
                    f1.WriteLog("Thread started.");
                }
            }
    
            public void Stop()
            {
                if (_notifyThread == null) return;
    
                // Set the stop event (causes the thread to exit
                _stopEvent.Set();
    
                // Wait for the thread to exit
                _notifyThread.Join(5000);
    
                // Set the thread object to null, so we create a new one in Start( )
                _notifyThread = null;
    
                f1.WriteLog("Thread stopped.");
            }
    
            /// <summary>
            /// Worker thread.  Waits for Notify or Stop events.  Displays change in thread state
            /// and/or exits.
            /// </summary>
            public void ThreadProc()
            {
                while (true)
                {
                    switch (WaitHandle.WaitAny(new WaitHandle[] { _stopEvent, _notifyEvent }))
                    {
                        case StopEventIndex: // Stop event has been set - exit thread
                            f1.WriteLog("Thread exiting: State = " + State);
                            return;
                        case NotifyEventIndex:  // Notify event has been set - display state change
                            f1.WriteLog("Thread notified: State = " + State);
                            if (State == NotifyState.X) Stop(); else DoSomething();
                            break;
                    }
                }
            }
    
            private Thread _notifyThread;
            private readonly AutoResetEvent _notifyEvent = new AutoResetEvent(false);
            private readonly AutoResetEvent _stopEvent = new AutoResetEvent(false);
    
            private int _state = (int)NotifyState.S1;
            private const int StopEventIndex = 0;
            private const int NotifyEventIndex = 1;
    
            public Form1 f1;
        }
    }

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured