Writing in the console while program is writing in this console - c#

Is it possible to write something in the console while the program is writing something in this console ? It can be useful when you rename, or remove some files, when you do a repetitive action, and the program is writing a lot in the console. Then you will be able to write a command to stop the execution of the repetitive action while the program is continuing to write in the console. I think it's not very clear, well I illustrated you this fact with the code which I think the most apt (but I precise that it doesn't work ;) ). We have 3 classes.
The main class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static bool m_Write;
public static bool write
{
get { return m_Write; }
set { m_Write = value; }
}
static void Main(string[] args)
{
int index = 0;
Console.ReadLine();
m_Write = true;
Reader reader = new Reader();
while (m_Write)
{
index++;
Writer writer = new Writer(index.ToString());
}
}
}
}
The reading class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Reader
{
private Thread m_Reading_Thread;
private string m_text_To_Read;
public Reader()
{
m_Reading_Thread = new Thread(new ThreadStart(Read));
m_Reading_Thread.Start();
}
public void Read()
{
m_text_To_Read = Console.ReadLine();
if (m_text_To_Read == "Stop")
{
Program.write = false;
}
}
}
}
And the writing class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Writer
{
private Thread m_Writing_Thread;
private string m_Text_To_Write;
public Writer(string text_To_Write)
{
m_Text_To_Write = text_To_Write;
m_Writing_Thread = new Thread(new ThreadStart(Write));
m_Writing_Thread.Start();
}
public void Write()
{
Console.WriteLine(m_Text_To_Write);
}
}
}

This isn't nearly as complicated as you're trying to make it. In general there are two ways you can do this. You can start a background thread to do the writing, and have the main thread block on the console waiting for the read, or you can have the main thread writing and have the background thread do the read. I like the first solution best:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(WriterFunc);
t.Start();
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the thread to stop writing
StopWriting.Set();
// And wait for the thread to exit
t.Join();
}
private static void WriterFunc()
{
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
}
}
Note that I used a ManualResetEvent here rather than a Boolean flag. An even better solution would be to use a CancellationToken. Using a flag can cause all kinds of interesting problems because the compiler might determine that the variable can't change (it assumes single-threaded access). Your thread might continue running even after the variable is changed.
If you want the main thread to do the writing, and the background thread to do the reading:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(ReaderFunc);
t.Start();
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
// Wait for the background thread to exit
t.Join();
}
private static void ReaderFunc()
{
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the main thread to stop writing
StopWriting.Set();
}
}

Something like this would work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var w = new Writer();
var r = new Reader();
while (!r.finish)
{
w.enabled = true;
string k = Console.ReadKey(false).KeyChar.ToString();
w.enabled = false;
string line = k + Console.ReadLine();
r.Read(line);
}
}
}
class Writer
{
public bool enabled = true;
public Writer()
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (a, b) =>
{
if(enabled)
Console.WriteLine("Test");
};
timer.Start();
}
}
class Reader
{
public bool finish = false;
public void Read(string line)
{
if (line == "stop")
{
finish = true;
}
}
}
}
Don't worry if the Writer writes above what you are typing, the Console.ReadLine() only considers what you have typed.

In the case of a console application, no two threads can write data to the screen at the exact same time.
AFAIK, in the above answer, the Writes()'s constructor is continuously executed until it finishes running. Then the control will be passed to the Reader(). So I don't think that works for what you need. Correct me if I am wrong.

Related

Multithreaded network application issues

I've looked at least a hundred threads and none gave me a solution to my specific problem so far and i got tired of looking,
I'm trying to emulate a runescape game server in C#,
I need to receive lots of connections and parse the requests and send out responses for them until the game has loaded all the files, and then proceed to the actual login protocol. this needs to be able to handle multiple connections per second, as in, i handle 3 basic protocols "jaggrab", "ondemand" and then regular game protocol i.e; login, player updating etc these are all handled on the same port
My Server class listens for connections on one thread, while on another thread my PipelineFactory handles the connections in a Queue one by one as fast as it can, meanwhile there is a TaskPool thread which is executing PoolableTask objects at their own individual intervals... this works fine except as soon as I accept a connection both the pool and server threads begin to block and furthermore, the server object stops listening for connections all together but the thread seems to keep running.. I assume this is because the TcpListener.Pending() is not being updated? but i cant seem to find a function to update this list in the docs or anywhere
I seem to be using the threads the way all the multithreading tutorials explain them to work? i dont really understand what im doing wrong.. heres the important parts of my code:
MainEntry.cs:
using System;
using System.Threading;
using gameserver.evt;
using gameserver.io;
using gameserver;
using gameserver.io.player;
public static class MainEntry
{
public static void Main(string[] args)
{
Console.WriteLine("Starting game server...");
new Thread(new ThreadStart(Server.Run)).Start();
new Thread(new ThreadStart(TaskPool.Run)).Start();
new Thread(new ThreadStart(PipelineFactory.Run)).Start();
//TODO maybe pool these
}
}
Server.cs:
using gameserver.io.player;
using gameserver.io.sql;
using gameserver.model;
using gameserver.model.player;
using util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using gameserver.io.player.pipeline;
using gameserver.evt;
using util.cache;
namespace gameserver.io
{
public class Server
{
public static TcpListener serverSocket;
public static Dictionary<int, Player> players = new Dictionary<int, Player>(Constants.MaxPlayers);
public static Database database;
public const int ListenPort = 43594;
public static bool running = true;
public static readonly Cache cache = new Cache(Constants.CacheDir);
public static void Bind()
{
serverSocket = new TcpListener(IPAddress.Parse("0.0.0.0"), ListenPort);
serverSocket.Start();
Console.WriteLine("Server started at 0.0.0.0:" + ListenPort);
}
public static void Run()
{
if (serverSocket == null)
Bind();
while(IsRunning())
{
Console.Write("serve");
var incoming = serverSocket.AcceptTcpClient();
if (incoming != null)
{
PipelineFactory.queue.Enqueue(new PlayerSocket(incoming));
}
Thread.Sleep(25);
}
}
private static void Destroy()
{
serverSocket.Stop();
//saveall players
//Environment.Exit(0);
}
public static bool Online(Player player)
{
if(player == null)
{
return false;
}
for (int i = 0; i < players.Count; i++)
{
if (player.username == players[i].username)
{
return true;
}
}
return false;
}
public static Database Database => database ?? (database = new Database());
public static bool IsRunning() => running;
}
}
TickPool.cs:
using gameserver.io;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace gameserver.evt
{
public class TaskPool
{
public static bool running = true;
public static List<PoolableTask> tasks = new List<PoolableTask>();
public static void Run()
{
do
{
long cur = Environment.TickCount;
Console.Write("tick");
foreach (PoolableTask t in tasks)
{
if (cur - t.last >= t.interval)
{
if (t == null)
continue;
t.Execute();
t.last = cur;
}
}
Thread.Sleep(100);
} while (Server.IsRunning());
}
public static void Add(PoolableTask task)
{
if(!tasks.Contains(task))
tasks.Add(task);
}
public static void Stop(PoolableTask task)
{
if(tasks.Contains(task))
tasks.Remove(task);
}
}
}
PipelineFactory.cs:
using gameserver.evt;
using gameserver.io.player.pipeline;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace gameserver.io.player
{
// simple but effective state machine for all non player model related protocol
public class PipelineFactory
{
public static Queue<PlayerSocket> queue = new Queue<PlayerSocket>(100);
private static readonly LoginPipe loginPipe = new LoginPipe();
private static readonly JaggrabPipe jgpipe = new JaggrabPipe();
private static readonly HandshakePipe handshake = new HandshakePipe();
private static readonly OnDemandPipe ondemand = new OnDemandPipe();
public static void Run()
{
while (Server.IsRunning())
{
Console.Write("pipe");
if (queue.Count() > 0)
{
var socket = queue.First();
if (socket == null || !socket.GetSocket().Connected
|| socket.state == PipeState.Play)
{
queue.Dequeue();
return;
}
switch (socket.state)
{
case PipeState.Handshake:
socket.currentPipeline = handshake;
break;
case PipeState.Jaggrab:
socket.currentPipeline = jgpipe;
break;
case PipeState.OnDemand:
socket.currentPipeline = ondemand;
break;
case PipeState.LoginResponse:
case PipeState.Block:
case PipeState.Finalize:
socket.currentPipeline = loginPipe;
break;
case PipeState.Disconnect:
//TODO: Database.saveForPlayer
socket.Close();
break;
}
try
{
if (socket.currentPipeline != null)
socket.state = socket.currentPipeline.HandleSocket(socket);
}
catch (Exception)
{
socket.Close();
queue.Dequeue();
}
}
Thread.Sleep(20);
}
}
}
}
The protocol itself really shouldn't matter just know it's all being handled asynchronously
I'm a java programmer at heart but trying to delve into C# head first so thats why my conventions might not be perfect, and I don't really know how to/dont understand intellisense documentation but at some point ill look into it
EDIT: I just wanna note that everything worked perfectly before i tried using threads to multithread this, when i had the PipelineFactory a PoolableTask object implementation, it could handle multiple connections etc and there was only the main thread calling 2 while loops handling everything in the whole server, i'm trying to spread out the load over the cpu but its not working out for me lol

Passing variables between 2 threads in a c# console application (newbie here)

I'm wondering how I can send a variable from one thread to another in a c# console application. For example,
using System;
using System.Threading;
namespace example
{
class Program
{
static void Main(string[] args)
{
int examplevariable = Convert.ToInt32(Console.ReadLine ());
Thread t = new Thread(secondthread);
t.Start();
}
static void secondthread()
{
Console.WriteLine(+examplevariable);
}
}
}
I want to make "secondthread" recognize "examplevariable".
There is an overload to Thread.Start() that takes a parameter as object. You can pass your main thread variable to that and cast it as your variable type
using System;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int examplevariable = Convert.ToInt32(Console.ReadLine());
Thread t = new Thread(secondthread);
t.Start(examplevariable);
}
static void secondthread(object obj)
{
int examplevariable = (int) obj;
Console.WriteLine(examplevariable);
Console.Read();
}
}
}
if you want to pass multiple variable then use a model class and use property binding like below
using System;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TestModel tm = new TestModel();
tm.examplevariable1 = Convert.ToInt32(Console.ReadLine());
tm.examplevariable2 = Console.ReadLine();
Thread t = new Thread(secondthread);
t.Start(tm);
}
static void secondthread(object obj)
{
TestModel newTm = (TestModel) obj;
Console.WriteLine(newTm.examplevariable1);
Console.WriteLine(newTm.examplevariable2);
Console.Read();
}
}
class TestModel
{
public int examplevariable1 { get; set; }
public string examplevariable2 { get; set; }
}
}
Hope this will help
An easy way to do this, but might not work in all scenarios, would be to define a static variable on the class and assign the value read in from the console to the static variable. Like so:
class Program
{
static int examplevariable;
static void Main(string[] args)
{
examplevariable = Convert.ToInt32(Console.ReadLine ());
Thread t = new Thread(secondthread);
t.Start();
}
static void secondthread()
{
Console.WriteLine(+examplevariable);
}
Also, see this question on how to pass parameters to a Thread:
ThreadStart with parameters

Serial Communication gets back wrong answer when something is running in the background (like browsing the hard drive)

I have some big trouble with serial requests.
Description from what i want:
establish a serial connection, send serial requests to 6 temperature
sensors one by one (this is done every 0,5 second in a loop)
the question and answer-destination is stored in a List array
every request is started in a separate thread so the gui does not bug
while the programme waits for the sensor-hardware to answer
My problem:
The connection and the request is working fine, but if I am browsing data at the local hard drive the answer from the sensor-unit gets destroyed (negative algebraic sign or value from other sensor or simply wrong value).
How does this happen or how can I solve this?
Where I guess the problem might be:
In the private void ReceiveThread() of class SerialCommunication
Here is my code:
Class CommunicationArray:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hardwarecommunication
{
public class CommunicationArray
{
public string request { get; set; }
public object myObject { get; set; }
public string objectType { get; set; }
}
}
Class SerialCommunication
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
namespace Hardwarecommunication
{
class SerialCommunication
{
Thread t2;
Thread t;
private SerialPort serialPort = new SerialPort("COM2", 115200, Parity.Even, 8, StopBits.One);
string serialAnswer = "";
private volatile bool _shouldStop;
private int counter;
List<CommunicationArray> ar = new List<CommunicationArray>();
object[] o = new object[3];
public void addListener(string request, object myObject, string objectType)
{
CommunicationArray sa = new CommunicationArray();
sa.request = request;
sa.myObject = myObject;
sa.objectType = objectType;
ar.Add(sa);
}
public void startListen()
{
t2 = new Thread(() => writeSerialPortThread());
t2.Start();
}
public void startSerialPort2()
{
try
{
serialPort.Open();
//MessageBox.Show("Connection opend!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
public void stopSerialPort2()
{
try
{
if (serialPort.IsOpen == true)
// Connection closed
serialPort.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void writeSerialPortThread()
{
string request = "";
for (int i = 0; i < ar.Count(); i++)
{
request = ar[i].request;
//request = ((object[])ar[0])[0].ToString();
//if (!t.IsAlive)
//{
try
{
t = new Thread(ReceiveThread);
_shouldStop = false;
//MessageBox.Show("start thread");
t.Start();
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
t.Join();
}
catch
{
}
Label tmpLabelObject = (Label)ar[i].myObject;
serialAnswer = serialAnswer.Replace("=", "");
if (tmpLabelObject.InvokeRequired)
{
MethodInvoker UpdateLabel = delegate
{
tmpLabelObject.Text = serialAnswer;
};
try
{
tmpLabelObject.Invoke(UpdateLabel);
}
catch
{
}
}
}
}
private void ReceiveThread()
{
//MessageBox.Show("in thread");
while (!_shouldStop)
{
serialAnswer = "";
try
{
//MessageBox.Show("in thread");
serialAnswer = serialPort.ReadTo("\r");
if (serialAnswer != "")
{
}
return;
}
catch (TimeoutException) { }
}
}
}
}
Class Form1 //to establish the connection and to start the Sensor request
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 Hardwarecommunication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private SerialCommunication serialCommunication1 = new SerialCommunication();
private void Form1_Load(object sender, EventArgs e)
{
//start up serial connection
serialCommunication1.startSerialPort2();
}
private void buttonStart_Click(object sender, EventArgs e)
{
timerRecord.Enabled = true;
if (this.buttonStart.Text == "Start")
this.buttonStart.Text = "Stop";
else
this.buttonStart.Text = "Start";
}
private void timerRecord_Tick(object sender, EventArgs e)
{
if (this.buttonStart.Text == "Stop")
{
this.serialCommunication1.startListen();
}
}
private void buttonFillRequestArray_Click(object sender, EventArgs e)
{
this.serialCommunication1.addListener("$0BR00\r" + "\r", this.labelResult0, "label0"); //request to the hardware
this.serialCommunication1.addListener("$0BR01\r" + "\r", this.labelResult1, "label1");
this.serialCommunication1.addListener("$01R00\r" + "\r", this.labelResult2, "label2");
this.serialCommunication1.addListener("$01R01\r" + "\r", this.labelResult3, "label3");
this.serialCommunication1.addListener("$01R02\r" + "\r", this.labelResult4, "label4");
}
}
}
I woud be happy about any try to fix the problem.
I coud also upload the solution as .zip but you can't test it at all because you do not have the sensor hardware.
Note: serialPort.Write(string) is a non-blocking store into the output buffer.
That means the following won't guarantee you've even finished writing your request before you stop listening for a response:
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
You could add:
while( serialPort.BytesToWrite > 0 ) Thread.Sleep(1); // force blocking
but it's ill advised.
One thing I'm wondering. There is only a single serial port here. Why do you want many different threads to work with it when you could manage the entire serial port interaction with a single thread? (Or at worse, 1 thread for input 1 thread for output)
To me it makes a lot more sense to store up requests into a queue of some kind and then peel them off one at a time for processing in a single thread. Responses could be similarly queued up or fired as events back to the caller.
EDIT: If you don't mind one read/write cycle at a time you could try:
string response;
lock(serialPort) {
// serialPort.DiscardInBuffer(); // only if garbage in buffer.
serialPort.Write(request);
response = serialPort.ReadTo("\r"); // this call will block till \r is read.
// be sure \r ends response (only 1)
}

How can I collect key presses while time is passing?

I'd like to write a loop that collects key presses (from the keyboard) and does an action every second or so. There would be some way of reading from the keyboard:
whenever (Console.KeyPressed != null) {
input_buffer.Add(Console.KeyPressed);
}
And there would be some loop happening:
while (!done) {
if (input_buffer.NotEmpty()) { do_stuff(input_buffer.Pop()); }
do_other_stuff();
wait(0.5 seconds);
}
So if the user presses a key, it gets dealt with during the next update. If they don't press a key, the next update happens anyhow.
If you're on .Net 4 you can use the code below. It uses ConcurrentQueue for storing the keypresses, ManualResetEventSlim for signaling, and Task from the Task Parallel Library for running the two code parts asynchronously.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Test
{
public class Program
{
private static ConcurrentQueue<ConsoleKeyInfo> _keypresses = new ConcurrentQueue<ConsoleKeyInfo>();
private static ManualResetEventSlim _stopEvent = new ManualResetEventSlim();
public static void Main()
{
Console.TreatControlCAsInput = true;
var keyReaderTask = Task.Factory.StartNew(ReadKeys);
var keyProcessingTask = Task.Factory.StartNew(ProcessKeys);
_stopEvent.Wait();
keyReaderTask.Wait();
keyProcessingTask.Wait();
}
public static void ReadKeys()
{
while (true)
{
var keyInfo = Console.ReadKey(true);
if (keyInfo.Modifiers == ConsoleModifiers.Control && keyInfo.Key == ConsoleKey.C)
{
break;
}
_keypresses.Enqueue(keyInfo);
}
_stopEvent.Set();
}
public static void ProcessKeys()
{
while (!_stopEvent.IsSet)
{
if (!_keypresses.IsEmpty)
{
Console.Write("Keys: ");
ConsoleKeyInfo keyInfo;
while (_keypresses.TryDequeue(out keyInfo))
{
Console.Write(keyInfo.KeyChar);
}
Console.WriteLine();
}
_stopEvent.Wait(1000);
}
}
}
}

How to Pass a variable to another Thread

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Taking data from Main Thread\n->");
string message = Console.ReadLine();
ThreadStart newThread = new ThreadStart(delegate { Write(message); });
Thread myThread = new Thread(newThread);
}
public static void Write(string msg)
{
Console.WriteLine(msg);
Console.Read();
}
}
}
You can also use a the CallContext if you have some data that you want to "flow" some data with your call sequence. Here is a good blog posting about LogicalCallContext from Jeff Richter.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Taking data from Main Thread\n->");
string message = Console.ReadLine();
//Put something into the CallContext
CallContext.LogicalSetData("time", DateTime.Now);
ThreadStart newThread = new ThreadStart(delegate { Write(message); });
Thread myThread = new Thread(newThread);
}
public static void Write(string msg)
{
Console.WriteLine(msg);
//Get it back out of the CallContext
Console.WriteLine(CallContext.LogicalGetData("time"));
Console.Read();
}
}
}
There is an overload to Thread.Start that lets you pass in a parameter.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Taking data from Main Thread\n->");
string message = Console.ReadLine();
Thread myThread = new Thread(Write);
myThread.Start(message);
}
public static void Write(object obj)
{
string msg = (string)obj;
Console.WriteLine(msg);
Console.Read();
}
}
One way to get the same effect of passing a variable to a thread is to make a classwide private data member of the type you wish to pass to the thread. Set this value to whatever you want before you start the thread. If you have many threads, you will need to put a lock on this classwide data member to prevent unexpected values. Or you can use .NET native Mutex functionality to control access to the variable.
For example (didn't test this, just wrote it up on the fly):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
private string threadVariable;
static void Main(string[] args)
{
Console.WriteLine("Taking data from Main Thread\n->");
string message = Console.ReadLine();
threadVariable = "stuff";
Thread myThread = new Thread(Write);
Thread.IsBackground = true;
Thread.Start();
}
public static void Write()
{
Console.WriteLine(stuff);
Console.Read();
}
}
}
If you are asking how to pass parameters to threads, refer to this:
http://www.yoda.arachsys.com/csharp/threads/parameters.shtml
I am not sure if I understand your question correctly, but the following MSDN article shows how to pass data to a thread in the way that you are doing it (i.e., via ThreadStart and a delegate):
Passing data to thread
using System;
using System.Threading;
class Test
{
static void Main()
{
// To start a thread using a static thread procedure, use the
// class name and method name when you create the ThreadStart
// delegate. Beginning in version 2.0 of the .NET Framework,
// it is not necessary to create a delegate explicitly.
// Specify the name of the method in the Thread constructor,
// and the compiler selects the correct delegate. For example:
//
// Thread newThread = new Thread(Work.DoWork);
//
ThreadStart threadDelegate = new ThreadStart(Work.DoWork);
Thread newThread = new Thread(threadDelegate);
newThread.Start();
// To start a thread using an instance method for the thread
// procedure, use the instance variable and method name when
// you create the ThreadStart delegate. Beginning in version
// 2.0 of the .NET Framework, the explicit delegate is not
// required.
//
Work w = new Work();
w.Data = 42;
threadDelegate = new ThreadStart(w.DoMoreWork);
newThread = new Thread(threadDelegate);
newThread.Start();
}
}
class Work
{
public static void DoWork()
{
Console.WriteLine("Static thread procedure.");
}
public int Data;
public void DoMoreWork()
{
Console.WriteLine("Instance thread procedure. Data={0}", Data);
}
}
I use a separate worker class and populate a member variable in the constructor, I then use a void method as my delegate that uses the private member variable:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Taking data from Main Thread\n->");
string message = Console.ReadLine();
WorkerClass workerClass = new WorkerClass(message);
ThreadStart newThread = new ThreadStart(workerClass.DoWork);
Thread myThread = new Thread(newThread);
myThread.Start();
Console.Read();
}
}
internal class WorkerClass
{
private string _workerVariable = "";
internal WorkerClass(string workerVariable)
{
_workerVariable = workerVariable;
}
internal void DoWork()
{
Console.WriteLine(_workerVariable);
}
}
}
One handy set of classes I wrote up in vb2005 would allow the easy creation of a delegate with one to four bound arguments and zero or one unbound arguments. A huge mess of copy/paste code, since .net doesn't support variadic generics, but one could create a MethodInvoker which would call foo(bar,boz) by saying (vb.net syntax, but the approach would be the same in C#):
theMethodInvoker = InvMaker.NewInv(addressof foo, bar, boz)
theMethodInvoker() ' Calls foo(bar,boz)
which would generate an object containing fields Param1 as BarType, Param2 as BozType, and theAction as Action(of BarType, BozType). It would set those fields to bar, boz, and foo, and return a MethodInvoker which would call doIt, a method which called theAction(Param1, Param2). If I needed an Action(of Integer), I would use:
theMethodInvoker = ActionMaker(of Integer).NewInv(addressof foo, bar, boz)
theMethodInvoker(9) ' Calls foo(9,bar,boz)
Really slick. Lambdas avoid the need for a cut-and-paste library, but their internal implementation is similar. I've read that Lambdas cause difficulty with edit-and-continue; I know my method does not.

Categories

Resources