I have a class which contains a method for receiving UDP data in a separate thread. I do this to avoid the main application (which is running in Unity3D) from stalling.
I need to pass the data that is received in the separate thread to another class, which runs on the original thread and, as such, is able to interact with Unity3D.
Here is roughly what the UDPReceiver looks like:
public class UDPReciever {
//...
public UDPReciever() {
m_Port = 12345;
m_Worker = new Thread(new ThreadStart(recvData));
m_Worker.IsBackground = true;
m_Worker.Start();
}
void recvData() {
m_UDPClient = new UdpClient(m_Port);
while (true) {
try {
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = (m_UDPClient.Receive(ref anyIP));
// TODO: Hand 'data' to NetworkController class (running in the original thread) for processing
} catch (Exception err) {
print(err.ToString());
}
}
}
}
This is roughly what the NetworkController class needs to look like. Ideally the "OnNewData" method would be called every time a new packet is received with the data passed as an argument.
public class NetworkController {
//...
void OnNewData(pData) {
// Process the data in this thread
}
}
How would I go about achieving this? Thanks in advance.
Here is how it could be done (not tested):
public class Dispatcher : MonoBehaviour
{
private static readonly BlockingCollection<Action> tasks = new BlockingCollection<Action>();
public static Dispatcher Instance = null;
static Dispatcher()
{
Instance = new Dispatcher();
}
private Dispatcher()
{
}
public void InvokeLater(Action task)
{
tasks.Add(task);
}
void FixedUpdate()
{
if (tasks.Count > 0)
{
foreach (Action task in tasks.GetConsumingEnumerable())
{
task();
}
}
}
}
...
NetworkController networkControllerInstance;
void recvData()
{
m_UDPClient = new UdpClient(m_Port);
while (true)
{
try
{
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = (m_UDPClient.Receive(ref anyIP));
Dispatcher.Instance.InvokeLater(() => networkControllerInstance.OnNewData(data));
}
catch (Exception err)
{
print(err.ToString());
}
}
}
EDIT:
A version that should be compliant with .Net 3.5:
public class Dispatcher : MonoBehaviour
{
private static readonly Queue<Action> tasks = new Queue<Action>();
public static Dispatcher Instance = null;
static Dispatcher()
{
Instance = new Dispatcher();
}
private Dispatcher()
{
}
public void InvokeLater(Action task)
{
lock (tasks)
{
tasks.Enqueue(task);
}
}
void FixedUpdate()
{
while (tasks.Count > 0)
{
Action task = null;
lock (tasks)
{
if (tasks.Count > 0)
{
task = tasks.Dequeue();
}
}
task();
}
}
}
EDIT 2:
if you want to avoid freezing the main thread during a too long period:
void FixedUpdate()
{
if (tasks.Count != 0)
{
Action task = null;
lock (tasks)
{
if (tasks.Count != 0)
{
task = tasks.Dequeue();
}
}
task();
}
}
Related
I have a project that without usign any form/button or nothing like that, connects with a Websocket and using async methods receives some message(on a form created by myself) that is supposed to appear on the top-right corner of the screen.
But this message can appear from time to time (2 or 3 minutes) on the screen if the websocket doesn't say that it must stop. And this message can be big enough, that in order to make it look better I make my message appear in more than one form.
It causes an impression that it's a notification. So my class that connects with the websocket and receives the message async, calls another class using a thread that is a controller. The purpose of the controller is from time to time, show that message in various new form() notifications and obviously don't do it if the websocket doesn't return any message.
But when i call the form.show the program stops working.
I've looked around stackoverflow already, but the ideas that i've found didn't seem to work.
Some say that I should use invoke, but it kept giving error saying that
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created", tried to solve like this: C# calling form.show() from another thread but it didn't work.
Some said that I should use .showDialog instead of .show, but it doesn't appear to be good, because it waits the window to be closed to terminate the method and as I said I need to open more than one notification at the same time.
Some said that the form was open with .show, but it was open for a very little period of time. But i couldn't notice if that was the case and even if it was i couldn't solve it. Well, what matter is that i'm stuck and i don't know what to do more.
Edited with Code:
//Main
Application.Run(new SocketService());
//SocketService class
public SocketService()
{
alerta = null;
while (true)
{
try
{
//Console.WriteLine("Nome do UsĂșario:" + Environment.UserName);
Thread.Sleep(2000);
Connect("ws://192.168.120.38:9091").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Login(webSocket);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (webSocket != null)
webSocket.Dispose();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
private static async Task Login(ClientWebSocket webSocket)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes( "{\"event\":\"loginBrowser\",\"data\":{\"login\":\"000000003077\",\"data\":\"1\"}}"));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
if (webSocket.State == WebSocketState.Open)
{
if (ShowMessage.created != true)
{
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
}
await Receive(webSocket);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
if (result.EndOfMessage)
{
message += encoder.GetString(buffer.ToArray());
SendMessage(message);
}
else
{
message += encoder.GetString(buffer.ToArray());
}
}
}
}
public static void ShowFormFromAnotherThread(string text)
{
_sync.Post(SendOrPostCallback, text);
}
private static void SendOrPostCallback(object state)
{
var form = new Notification();
form.Text = (string)state;
form.Show();
}
private static void ThreadProc()
{
while (true)
{
Thread.Sleep(2000); // wait imitation
ShowFormFromAnotherThread("HI");
}
}
/*Notification is my form and depending on where I put this part:
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
Or i doesn't call login or doesn't enter receive() method or the best case It receives the information
calls the threadProc and the ShowFormFromAnotherThread but doesn't enter SednOrPostCallBack*/
using System.Threading;
using System.Windows.Forms;
namespace ConsoleThreadSync
{
internal class Program
{
private static void Main(string[] args)
{
Application.Run(new App());
}
}
public class App : ApplicationContext
{
private readonly SynchronizationContext _sync;
public App()
{
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
}
public void ShowFormFromAnotherThread(string text)
{
_sync.Post(SendOrPostCallback, text);
}
private void SendOrPostCallback(object state)
{
var form = new Form1();
form.Text = (string)state;
form.Show();
}
private void ThreadProc()
{
while (true)
{
Thread.Sleep(2000); // wait imitation
ShowFormFromAnotherThread("HI");
}
}
}
}
Try to call this:
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
from a contructor SocketService() and not from async methods. This is an initialization code and it must call from main thread.
Okay, after reading a little bit more, the solution that kind worked out was this one, but the only way of using the
.show from the notifician is to use the Application.DoEvents and I've been warned from the sources that I've looked into
that this method should not be used, unless is the only option, because It can cause some problems with the Threads and other things.
So unless someone can give me another hint or clue about what to do, I have two options or use this method and try to fix some other bug
that It can cause or use the .showDialog because don't know why It works without any other problem, but to use .showDialog I've
to use another thread where I create and show the notification because if I don't do, the loop will stop at each iteration
in order to wait the .showDialog be closed. And as it isn't a problem I want to avoid using a lot of threads, because it can cause
another problem with the sync between them:
namespace ReiDoCSharp
{
class ShowMessage
{
private static RootObject alerta;
public static bool created;
private static int startPosition;
public static void setStartPosition(int start)
{
if (start < startPosition)
{
startPosition = start;
}
}
public RootObject getAlerta()
{
return ShowMessage.alerta;
}
public void setAlerta(RootObject root)
{
ShowMessage.alerta = root;
}
private static void DoWork()
{
while (true)
{
if (created != true)
{
created = true;
}
if (alerta != null)
{
string mensagem = "";
if ((alerta.data.Informacoes[1] != "") && (alerta.data.Informacoes[1] != null))
{
mensagem += alerta.data.Informacoes[1];
}
if ((alerta.data.Informacoes[0] != "") && (alerta.data.Informacoes[0] != null))
{
mensagem += alerta.data.Informacoes[0];
}
if (mensagem != "")
{
startPosition = 5;
string[] messages = mensagem.Split(new[] { "<br><br>" }, StringSplitOptions.None);
foreach (string message in messages)
{
Notification popup = new Notification();
popup.label1.Text = message;
popup.TopMost = true;
popup.Show();
Application.DoEvents();
/*Solution with the ShowDialog would be:
Task.Run(() => showNotification(message));
*/
}
}
}
Thread.Sleep(5000);
}
}
//Then I won't need to use Application.DoEvents, but would have to create more threads
private static Task showNotification(string message)
{
Notification popup = new Notification();
popup.label1.Text = message;
popup.TopMost = true;
popup.ShowDialog();
}
public static Task createPopupsAsync()
{
Task.Run(() => DoWork());
}
}
}
namespace ReiDoCSharp
{
class SocketService
{
private static object consoleLock = new object();
private const bool verbose = true;
private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(3000);
private static UTF8Encoding encoder = new UTF8Encoding();
private static string message;
private static RootObject alerta;
public SocketService()
{
Begin();
}
public static void Begin()
{
alerta = null;
while (true)
{
try
{
Thread.Sleep(2000);
Connect("ws://192.168.120.38:9091").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Login(webSocket);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (webSocket != null)
webSocket.Dispose();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
private static async Task Login(ClientWebSocket webSocket)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes("{\"event\":\"loginBrowser\",\"data\":{\"OPERADOR\":\"000000003077\",\"NRORG\":\"1\"}}"));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
if (webSocket.State == WebSocketState.Open)
{
Task.Factory.StartNew(() => ShowMessage.createPopupsAsync());
await Receive(webSocket);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
if (result.EndOfMessage)
{
message += encoder.GetString(buffer.ToArray());
SendMessage(message);
}
else
{
message += encoder.GetString(buffer.ToArray());
}
}
}
}
private static void LogStatus(bool receiving, byte[] buffer, int length, string assunto)
{
lock (consoleLock)
{
Console.ForegroundColor = receiving ? ConsoleColor.Green : ConsoleColor.Yellow;
if (verbose)
{
Console.WriteLine(encoder.GetString(buffer) + " " + assunto);
}
Console.ResetColor();
}
}
private static void SendMessage(string message)
{
message = message.Replace("event", "evento");
message = message.Replace("\0", "");
JavaScriptSerializer js = new JavaScriptSerializer();
RootObject mess = js.Deserialize<RootObject>(message);
if (mess.data.Informacoes[1] != "")
{
mess.data.Informacoes[1] += "<br>";
}
if (alerta == null)
{
alerta = mess;
}
else
{
if ((mess.data.Quantidade[0] != 0) && (mess.data.Quantidade == null))
{
if ((mess.data.Quantidade[0] == -1) && (mess.data.Informacoes[0] == ""))
{
alerta = null;
}
else
{
alerta = mess;
}
}
else if (mess.data.Quantidade[0] == 0)
{
alerta = null;
}
if ((mess.data.Quantidade[1] != 0) && (mess.data.Informacoes[1] != ""))
{
alerta = mess;
}
}
new ShowMessage().setAlerta(alerta);
message = "";
}
}
}
I have written a simple Producer-Consumer queue. It keeps throwing an error that the safe handle is disposed.
namespace ConsoleApplication1
{
public class ProducerConsumer:IDisposable
{
private Queue<string> tasks = new Queue<string>();
Thread work;
readonly object obj=new object();
EventWaitHandle wh = new AutoResetEvent(true);
public ProducerConsumer()
{
work = new Thread(doWork);
work.Start();
}
public void doWork()
{
while (true)
{
string task = null;
wh.WaitOne();
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
}
}
}
public void ShutDown()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void Dispose()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void AddTask(string str)
{
lock(obj){tasks.Enqueue(str);wh.Set();}
}
}
class Program
{
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
ProducerConsumer prod = new ProducerConsumer();
prod.AddTask("Started");
for (int i = 0; i < 5; i++) prod.AddTask(Convert.ToString(i));
prod.AddTask("Done");
prod.ShutDown();
Console.ReadLine();
}
}
}
If I change the wh.WaitOne() to the else part then it starts working
public void doWork()
{
while (true)
{
string task = null;
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
else
wh.WaitOne();
}
}
}
Can somebody throw a light on why this is happening?
When using the StartNew() method to kick off a process on a new thread, I need to figure out how to make another call into this object in that same thread (I assume this would be some sort of Join operation?).
The following example is dumbed down to illustrate the meat of what I am trying to do. I am well aware it is severely lacking in basic concurrency considerations. But I didn't want to cloud the code with all of that logic, so please forgive me on that.
The following console app shows what I am trying to accomplish. Assume on the StartNew() call a new thread with ID 9976 is created and the method invoked there. I would like the subsequent call to ProcessImmediate() in the file system watcher change event handler to be made on thread 9976 as well. As it stands, the call would share the same thread that is used for the file system watcher change event.
Can this be done, and if so, how?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
public void Run()
{
_activity = new Activity();
// start activity on a new thread
Task.Factory.StartNew(() => _activity.Go());
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "c:\temp";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity
{
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
System.Threading.Thread.Sleep(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
DoSomethingInteresting();
}
public bool Stop { get; set; }
}
}
* UPDATE *
Thanks for the excellent responses. I took Mike's suggestion and implemented it for my console app. Below is the full working code which also includes the use of a cancellation token. I post this in case someone else might find it useful.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
runner.Stop();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
private CancellationTokenSource _cts = new CancellationTokenSource();
public void Stop() { _cts.Cancel(); }
public void Run()
{
_activity = new Activity();
// start activity on a new thread
var task = new Task(() => _activity.Go(_cts.Token), _cts.Token, TaskCreationOptions.LongRunning);
task.Start();
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "C:\\Temp\\FileSystemWatcherPath";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go(CancellationToken ct)
{
Thread.CurrentThread.Name = "Go";
while (!ct.IsCancellationRequested)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(5000);
}
Console.WriteLine("Exiting");
}
protected virtual void DoSomethingInteresting()
{
Console.WriteLine(string.Format("Doing Something Interesting on thread {0}", Thread.CurrentThread.ManagedThreadId));
}
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
_processing.Set();
}
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
}
First, you should use TaskCreationOptions.LongRunning if you are creating a task that will not complete quickly. Second, use an AutoResetEvent to signal the waiting thread to wake up. Note that below ProcessImmediate will return before DoSomethingInteresting has completed running on the other thread. Example:
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
User mike has given a better solution, which will be appropriate when you like to call the same method immediately. If you want to call a different methods immediately I'll expand mike's answer to achieve that.
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
private ConcurrentQueue<Action> actionsToProcess = new ConcurrentQueue<Action>();
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
while(!actionsToProcess.IsEmpty)
{
Action action;
if(actionsToProcess.TryDeque(out action))
action();
}
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate(Action action)
{
actionsToProcess.Enqueue(action);
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
To execute different methods on the same thread you can use a message loop that dispatches incoming requests. A simple option would be to use the event loop scheduler of the Reactive Extensions and to "recursively" schedule your Go() function - if in the mean time a different operation is scheduled it would be processed before the next Go() operation.
Here is a sample:
class Loop
: IDisposable
{
IScheduler scheduler = new EventLoopScheduler();
MultipleAssignmentDisposable stopper = new MultipleAssignmentDisposable();
public Loop()
{
Next();
}
void Next()
{
if (!stopper.IsDisposed)
stopper.Disposable = scheduler.Schedule(Handler);
}
void Handler()
{
Thread.Sleep(1000);
Console.WriteLine("Handler: {0}", Thread.CurrentThread.ManagedThreadId);
Next();
}
public void Notify()
{
scheduler.Schedule(() =>
{
Console.WriteLine("Notify: {0}", Thread.CurrentThread.ManagedThreadId);
});
}
public void Dispose()
{
stopper.Dispose();
}
}
static void Main(string[] args)
{
using (var l = new Loop())
{
Console.WriteLine("Press 'q' to quit.");
while (Console.ReadKey().Key != ConsoleKey.Q)
l.Notify();
}
}
I have one thread, that is sending data stored in a buffer of type List< string> via tcp. Another thread is writing into the buffer. As I am not very familiar with c# I'd like to know how I should use lock or Mutex correctly.
This is the code I'd like to use eventually:
while(buffer.isLocked())
{
buffer.wait();
}
buffer.lockBuffer();
buffer.add(tcpPacket);
buffer.unlockBuffer();
buffer.notify();
This is my current code. I hope someone can help me complete it.
public class Buffer
{
private Mutex mutex;
private List<string> buffer;
private bool locked = false;
public Buffer()
{
mutex = new Mutex(false);
buffer = new List<string>();
}
public bool isLocked()
{
return locked;
}
public void lockBuffer()
{
if (!locked)
{
//...
locked = true;
}
}
public void unlockBuffer()
{
if(locked)
{
mutex.ReleaseMutex();
locked = false;
}
}
public void wait()
{
mutex.WaitOne();
}
public void notify()
{
//...
}
}
It would be better if you use System.Collections.Concurrent.BlockingCollection. It doesn't require an external sync.
For those who don't use 4.0
using System;
using System.Collections.Generic;
using System.Threading;
namespace MyCollections
{
public class BlockingQueue<T> : IDisposable
{
Queue<T> _Queue = new Queue<T>();
SemaphoreSlim _ItemsInQueue = null;
SemaphoreSlim _FreeSlots = null;
int _MaxItems = -1;
public BlockingQueue(int maxItems=Int32.MaxValue)
{
_MaxItems = maxItems;
_ItemsInQueue = new SemaphoreSlim(0, maxItems);
_FreeSlots = new SemaphoreSlim(maxItems, maxItems);
}
public void Dispose()
{
if (_ItemsInQueue != null) _ItemsInQueue.Dispose();
if (_FreeSlots != null) _FreeSlots.Dispose();
}
public int Count
{
get { return _ItemsInQueue.CurrentCount; }
}
public void Add(T item)
{
if(_MaxItems != Int32.MaxValue) _FreeSlots.Wait();
lock (this)
{
_Queue.Enqueue(item);
_ItemsInQueue.Release();
}
}
public T Take()
{
T item = default(T);
_ItemsInQueue.Wait();
lock (this)
{
item = _Queue.Dequeue();
if (_MaxItems != Int32.MaxValue) _FreeSlots.Release();
}
return item;
}
}
}
The following code is not thread-safe. If two threads are entering this method at the same time, both might pass the if condition successfully.
public void lockBuffer()
{
if (!locked)
{
//...
locked = true;
}
}
You simply might want to do something like this:
lock (_sycnObject)
{
buffer.lockBuffer();
buffer.add(tcpPacket);
buffer.unlockBuffer();
buffer.notify();
}
I don't think you're doing something sophisticated that requires more than the simple to use lock-statement.
I wouldn't use Mutexes since I suppose you aren't dealing with multiple processes synchronization. Locks are pretty fine and simpler to implement:
class Buffer
{
private readonly object syncObject = new object();
private readonly List<string> buffer = new List<string>();
public void AddPacket(string packet)
{
lock (syncObject)
{
buffer.Add(packet);
}
}
public void Notify()
{
// Do something, if needed lock again here
// lock (syncObject)
// {
// Notify Implementation
// }
}
}
The usage is obviously (as you requested):
var myBuffer = new Buffer();
myBuffer.Add("Hello, World!");
myBuffer.Notify();
I'm attempting to make my simple C# graphics library multi-threaded. However, after the introduction of this code:
/* foreach (IAffector affector in affectorLookup.Values)
affector.Update(timestep); */
taskManager.Value = timestep; taskManager.Start();
foreach (IAffector affector in affectorLookup.Values)
taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();
the simulation starts experiencing sharp lag-spikes, which seem to originate in TaskHandler.Run (I can't tell for sure, because adding the previous code makes my code profiler ignore anything outside TaskHandler.Run).
The task manager:
public class TaskManager
{
public delegate void MethodDel(float timestep);
private Queue<MethodDel> queue;
private List<TaskHandler> handlers;
private float value;
public float Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
public TaskManager()
{
this.queue = new Queue<MethodDel>();
this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);
for (int t = 0; t < this.handlers.Capacity; ++t)
this.handlers.Add(new TaskHandler(this));
this.value = 0;
}
public void Start()
{
foreach (var handler in handlers)
handler.Start();
}
public void Stop()
{
lock (queue)
queue.Clear();
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void StopWhenDone()
{
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void AddToQueue(MethodDel method)
{
lock (queue)
queue.Enqueue(method);
}
public bool GetFromQueue(out MethodDel method)
{
lock (queue)
{
if (queue.Count == 0) { method = null; return false; }
method = queue.Dequeue();
return true;
}
}
public int GetQueueCount()
{
return queue.Count;
}
internal void Wait()
{
// Have to wait for them one at a time because the main thread is STA.
WaitHandle[] waitHandles = new WaitHandle[1];
// for (int t = 0; t < handlers.Count; ++t)
// waitHandles[t] = handlers[t].WaitHandle;
// WaitHandle.WaitAll(waitHandles);
for (int t = 0; t < handlers.Count; ++t)
{ waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
}
}
And the task handler:
public class TaskHandler
{
private TaskManager manager;
private Thread thread;
private bool stopWhenDone;
private ManualResetEvent waitHandle;
public ManualResetEvent WaitHandle
{
get
{
return waitHandle;
}
}
public TaskHandler(TaskManager manager)
{
this.manager = manager;
}
public void Start()
{
waitHandle = new ManualResetEvent(false);
stopWhenDone = false;
thread = new Thread(Run);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
}
public void StopWhenDone()
{
this.stopWhenDone = true;
}
// Possible source of slowdown
private void Run()
{
TaskManager.MethodDel curMethod;
while (!stopWhenDone || manager.GetQueueCount() > 0)
{
if (manager.GetFromQueue(out curMethod))
{
curMethod(manager.Value);
}
}
waitHandle.Set();
}
}
Starting a thread is a heavy operation. Not sure if it's as heavy as you are experiencing, but that could be it. Also, having all your processing run parallel can be putting a big strain on your system with possibly little benefit...
I'm going to venture that the spikes have something to do with waitHandle.Set();
I like the overall design, but I have not used WaitHandle before, so I am unsure how this interacts with your design.