This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 8 years ago.
I have a weird problem that i can't solve, i have a form that i open within another form, as soon as i open that form and since there is no event to fire after page finish loading, at the form load event i set a Timer that will start after 5sec. The timer will trigger a Task that will download files, downloading files is updated in a progressbar. The problem is that anything i try to change while Tasks are running doesn't update the GUI and will only change GUI after all Tasks finishes, note that the progressbar gets updated fine. Here is my code:
private void frm_HosterDownloader_Load(object sender, EventArgs e)
{
StartDownloadTimer = new Timer();
StartDownloadTimer.Tick += StartDownloadTimer_Tick;
StartDownloadTimer.Interval = 5000;
StartDownloadTimer.Start();
}
void StartDownloadTimer_Tick(object sender, EventArgs e)
{
StartDownload();
StartDownloadTimer.Stop();
}
private void StartDownload()
{
int counter = 0;
Dictionary<string, string> hosters = Hosters.GetAllHostersUrls();
progressBar_Download.Maximum = hosters.Count * 100;
progressBar_Download.Minimum = 0;
progressBar_Download.Value = 0;
foreach (KeyValuePair<string, string> host in hosters)
{
//Updating these tow lables never works, only when everything finishes
lbl_FileName.Text = host.Key;
lbl_NumberOfDownloads.Text = (++counter).ToString() + "/" + hosters.Count().ToString();
Task downloadTask = new Task(() =>
{
Downloader downloader = new Downloader(host.Value, string.Format(HostersImagesFolder + #"\{0}.png", IllegalChars(host.Key)));
downloader.HosterName = host.Key;
downloader.DownloadFinished += downloader_DownloadFinished;
downloader.Execute();
});
downloadTask.Start();
downloadTask.Wait();
}
}
void downloader_DownloadFinished(object sender, ProgressEventArgs e)
{
progressBar_Download.Value = progressBar_Download.Value + (int)e.ProgressPercentage;
}
I tired putting the tow label statments within the Task and even tried to pass them as an argument to be updated in the DownloadFinish event but no luck.
Edit:
Here is the Downloader Class:
public class Downloader : DownloaderBase
{
public string HosterName { set; get; }
/// <summary>
/// Initializes a new instance of the <see cref="Downloader"/> class.
/// </summary>
/// <param name="hoster">The hoster to download.</param>
/// <param name="savePath">The path to save the video.</param>
/// <param name="bytesToDownload">An optional value to limit the number of bytes to download.</param>
/// <exception cref="ArgumentNullException"><paramref name="video"/> or <paramref name="savePath"/> is <c>null</c>.</exception>
public Downloader(string hosterUrl, string savePath, int? bytesToDownload = null)
: base(hosterUrl, savePath, bytesToDownload)
{ }
/// <summary>
/// Occurs when the downlaod progress of the file file has changed.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadProgressChanged;
/// <summary>
/// Starts download.
/// </summary>
/// <exception cref="IOException">The video file could not be saved.</exception>
/// <exception cref="WebException">An error occured while downloading the video.</exception>
public override void Execute()
{
this.OnDownloadStarted(new ProgressEventArgs(0, HosterName));
var request = (HttpWebRequest)WebRequest.Create(this.HosterUrl);
if (this.BytesToDownload.HasValue)
{
request.AddRange(0, this.BytesToDownload.Value - 1);
}
try
{
// the following code is alternative, you may implement the function after your needs
request.Timeout = 100000;
request.ReadWriteTimeout = 100000;
request.ContinueTimeout = 100000;
using (WebResponse response = request.GetResponse())
{
using (Stream source = response.GetResponseStream())
{
using (FileStream target = File.Open(this.SavePath, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = source.Read(buffer, 0, buffer.Length)) > 0)
{
target.Write(buffer, 0, bytes);
copiedBytes += bytes;
var eventArgs = new ProgressEventArgs((copiedBytes * 1.0 / response.ContentLength) * 100, HosterName);
if (this.DownloadProgressChanged != null)
{
this.DownloadProgressChanged(this, eventArgs);
if (eventArgs.Cancel)
{
cancel = true;
}
}
}
}
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
Execute();
}
this.OnDownloadFinished(new ProgressEventArgs(100, HosterName));
}
}
public abstract class DownloaderBase
{
/// <summary>
/// Initializes a new instance of the <see cref="DownloaderBase"/> class.
/// </summary>
/// <param name="hosterUrl">The video to download/convert.</param>
/// <param name="savePath">The path to save the video/audio.</param>
/// /// <param name="bytesToDownload">An optional value to limit the number of bytes to download.</param>
/// <exception cref="ArgumentNullException"><paramref name="hosterUrl"/> or <paramref name="savePath"/> is <c>null</c>.</exception>
protected DownloaderBase(string hosterUrl, string savePath, int? bytesToDownload = null)
{
if (hosterUrl == null)
throw new ArgumentNullException("video");
if (savePath == null)
throw new ArgumentNullException("savePath");
this.HosterUrl = hosterUrl;
this.SavePath = savePath;
this.BytesToDownload = bytesToDownload;
}
/// <summary>
/// Occurs when the download finished.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadFinished;
/// <summary>
/// Occurs when the download is starts.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadStarted;
/// <summary>
/// Gets the number of bytes to download. <c>null</c>, if everything is downloaded.
/// </summary>
public string HosterUrl { get; set; }
/// <summary>
/// Gets the number of bytes to download. <c>null</c>, if everything is downloaded.
/// </summary>
public int? BytesToDownload { get; private set; }
/// <summary>
/// Gets the path to save the video/audio.
/// </summary>
public string SavePath { get; private set; }
/// <summary>
/// Starts the work of the <see cref="DownloaderBase"/>.
/// </summary>
public abstract void Execute();
protected void OnDownloadFinished(ProgressEventArgs e)
{
if (this.DownloadFinished != null)
{
this.DownloadFinished(this, e);
}
}
protected void OnDownloadStarted(ProgressEventArgs e)
{
if (this.DownloadStarted != null)
{
this.DownloadStarted(this, e);
}
}
}
It is not useful to use a Task this way:
downloadTask.Start();
downloadTask.Wait();
The Wait() will block the calling code and that is handling an event. Your downloads are effectively executing on the main GUI thread, blocking it.
The solution is
//downloadTask.Wait();
You don't seem to need it.
There is rarely a good reason to use threads (either new ones you create or threadpool) to do IO bound work. Here is an async alternative to your synchronous Execute method:
public async Task ExecuteAsync()
{
this.OnDownloadStarted(new ProgressEventArgs(0, HosterName));
var httpClient = new HttpClient();
var request = (HttpWebRequest)WebRequest.Create(this.HosterUrl);
if (this.BytesToDownload.HasValue)
{
request.AddRange(0, this.BytesToDownload.Value - 1);
}
try
{
request.Timeout = 100000;
request.ReadWriteTimeout = 100000;
request.ContinueTimeout = 100000;
var response = await httpClient.SendAsync(request);
var responseStream = await response.Content.ReadAsStreamAsync();
using (FileStream target = File.Open(this.SavePath, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await target.WriteAsync(buffer, 0, bytes);
copiedBytes += bytes;
var eventArgs = new ProgressEventArgs((copiedBytes * 1.0 / response.ContentLength) * 100, HosterName);
if (this.DownloadProgressChanged != null)
{
this.DownloadProgressChanged(this, eventArgs);
if (eventArgs.Cancel)
{
cancel = true;
}
}
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
}
this.OnDownloadFinished(new ProgressEventArgs(100, HosterName));
}
Now, there is no need to use Task.Wait or create a new Task. IO bound work is asynchronous by nature. In a combination with the new async-await keywords in C# 5, you can keep your UI responsive through the whole time, as each await yields control back to the calling method, and frees your winforms message pump to process more messasges in the meanwhile.
private async void frm_HosterDownloader_Load(object sender, EventArgs e)
{
await Task.Delay(5000);
await StartDownloadAsync();
}
private async Task StartDownloadAsync()
{
int counter = 0;
Dictionary<string, string> hosters = Hosters.GetAllHostersUrls();
progressBar_Download.Maximum = hosters.Count * 100;
progressBar_Download.Minimum = 0;
progressBar_Download.Value = 0;
var downloadTasks = hosters.Select(hoster =>
{
lbl_FileName.Text = hoster.Key;
lbl_NumberOfDownloads.Text = (++counter).ToString() + "/" + hosters.Count().ToString();
Downloader downloader = new Downloader(host.Value, string.Format(HostersImagesFolder + #"\{0}.png", IllegalChars(host.Key)));
downloader.HosterName = host.Key;
downloader.DownloadFinished += downloader_DownloadFinished;
return downloader.ExecuteAsync();
});
return Task.WhenAll(downloadTasks);
}
Note i changed your timer to a Task.Delay, since it internally uses a timer and you only need it to execute once.
If you want more on the use of async-await, you can start here.
Related
I want to be able to play multiple sounds at once. I tried this using multi threading but found they would still play one after the other. Is there a way to get them to play at the same time?
static void Main(string[] args)
{
Console.WriteLine("Hello World");
Thread th = new Thread(playSound);
Thread th1 = new Thread(playSound1);
th.Start();
th1.Start();
}
public static void playSound()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(#"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
s1.PlaySync();
}
public static void playSound1()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(#"c:\Users\Ben\Documents\c#\note_e.wav");
s1.Load();
s1.PlaySync();
}
}
How about if we schedule a parallel execution.
Like that:
var files = new List<string>() {"note_1.wav", "note_2.wav"};
Parallel.ForEach(files, (currentFile) =>
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(currentFile);
s1.Load();
s1.PlaySync();
});
Try using
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
Usage:
mciSendString(#"open path\to\your\file.wav type waveaudio alias anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(#"play anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(#"open path\to\your\anotherFile.wav type waveaudio alias anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
mciSendString(#"play anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
You can't start threads at exactly same time but in this case you can improve your code by loading the sounds before starting the threads:
static void Main(string[] args)
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(
#"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
System.Media.SoundPlayer s2 = new System.Media.SoundPlayer(
#"c:\Users\Ben\Documents\c#\note_e.wav");
s2.Load();
Console.WriteLine("Hello World");
Thread th = new Thread(() => playSound(s1));
Thread th1 = new Thread(() => playSound(s2));
th.Start();
th1.Start();
}
public static void playSound(System.Media.SoundPlayer s)
{
s.PlaySync();
}
Otherwise you'll need to implement a synchronization system.
The best way is to use DirectX xAudio2 via SharpDX , you can find it here:
https://github.com/sharpdx/SharpDX-Samples/tree/master/Desktop/XAudio2/PlaySound
It works perfectly , but it would be a bit tricky to make the code better, by utilizing Events & tasks instead of the while loop and sync execution, and to be able to load the xAudio2 graph dynamically to change the sound without reconstructing the whole graph.
Add these Nuget packages SharpDx
SharpDX.MediaFoundation
SharpDx.Xaudio2
and use code below, it uses DirectX xAudio2 and constructs a graph with a mixer, it can play WAV 8 and 16 as well
public class SoundServices : IDisposable
{
/// <summary>
/// Gets current sound file
/// </summary>
string SoundFile { get; }
/// <summary>
/// Gets current sound stream
/// </summary>
public Stream SoundStream { get; private set; }
/// <summary>
/// Gets/Sets looping option, sound will loop if set to true.
/// </summary>
public bool IsLooping { get; private set; }
/// <summary>
/// Holds the message of last error if any, check it if some feature fails
/// </summary>
public string LastErrorMsg { get; private set; }
//Play control flags
bool IsPlaying = false;
bool IsUserStop = false;
AutoResetEvent Playing;
float CurrentVolume = 1.0f;
bool IsInitialized = false;
SoundStream stream;
WaveFormat waveFormat;
AudioBuffer buffer;
SourceVoice sourceVoice;
XAudio2 xaudio2;
MasteringVoice masteringVoice;
/// <summary>
/// Initializes an instance by creating xAudio2 graph and preparing audio Buffers
/// </summary>
/// <param name="soundStream">WAV format sound stream</param>
/// <param name="loop">Bool indicating to loop sound playing or not</param>
public SoundServices(Stream soundStream, bool loop)
{
try
{
if (soundStream == null)
{
throw new ArgumentNullException("soundStream", "Null is not allowed, please specify a valid stream");
}
SoundStream = soundStream;
SoundFile = null;
IsLooping = loop;
//Playing = new ManualResetEvent(false);
Playing = new AutoResetEvent(false);
BuildxAudio2Graph();
}
catch (Exception e)
{
throw e;
}
}
#region Sound Play API
/// <summary>
/// Plays the sound stream loaded during initialization
/// </summary>
/// <returns>Task of sound playing</returns>
public Task PlaySound()
{
return Task.Factory.StartNew(() =>
{
PlayRepeatAsync();
});
}
/// <summary>
/// Task for starting play of sound buffers using xAudio2 graph
/// </summary>
void PlayRepeatAsync()
{
try
{
IsPlaying = true;
if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
sourceVoice.Start();
Playing.WaitOne();
sourceVoice.Stop();
IsPlaying = false;
sourceVoice.FlushSourceBuffers();
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
}
catch (Exception e)
{
LastErrorMsg = "PlayRepeatAsync(): "+ e.Message;
IsPlaying = false;
}
}
/// <summary>
/// Initializes xAudio2 graph to be used for sound playing
/// </summary>
void BuildxAudio2Graph()
{
try
{
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//xAudio2 graph creation step (1) Create XAudio2 device
xaudio2 = new XAudio2();
//xAudio2 graph creation step (2) Create MasteringVoice and connect it to device
//*Note: You must use aMasteringVoice to connect to xAudioDevice
masteringVoice = new MasteringVoice(xaudio2);
//SetVolume(CurrentVolume);
//xAudio2 graph creation step (3) Prepare sourceVoice
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
// Adds a callback check buffer end and Looping option
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
IsInitialized = true;
}
catch (Exception e)
{
LastErrorMsg = "BuildxAudio2Graph(): " + e.Message;
IsInitialized = false;
}
}
/// <summary>
/// Recreates source buffer allowing sound change or loop change
/// </summary>
void RecreateBuffer()
{
try
{
SoundStream.Seek(0, SeekOrigin.Begin);
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.FlushSourceBuffers();
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
}
catch (Exception e)
{
LastErrorMsg = "RecreateBuffer(): " + e.Message;
}
}
/// <summary>
/// Changes current audio to a new one
/// </summary>
/// <param name="soundStream">a stream from wav file</param>
public void ChangeSoundTo(Stream soundStream, bool Loop)
{
try
{
IsLooping = Loop;
SoundStream = soundStream;
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
RecreateBuffer();
}
catch (Exception e)
{
IsInitialized = false;
LastErrorMsg = "ChangeSoundTo(): " + e.Message;
}
}
/// <summary>
/// Set loop ot no loop
/// </summary>
/// <param name="loop">True = Loop forever, false = play till end</param>
public void SetLooping(bool loop)
{
if (IsPlaying)
Stop();
IsLooping = loop;
RecreateBuffer();
}
#endregion
/// <summary>
/// Immediately Stops currently playing sound
/// </summary>
public void Stop()
{
try
{
if (IsPlaying)
{
IsUserStop = true;
Playing.Set();
}
}
catch (Exception e)
{
LastErrorMsg = "Stop(): " + e.Message;
}
}
/// <summary>
/// Gets Current Volume
/// </summary>
/// <returns>Current volume</returns>
public float GetVolume()
{
float current = 0.0f;
try
{
if (sourceVoice == null || sourceVoice.IsDisposed) return CurrentVolume;
sourceVoice.GetVolume(out current);
}
catch (Exception e)
{
LastErrorMsg = "GetVolume(): " + e.Message;
}
return current;
}
/// <summary>
/// Sets the current volume
/// </summary>
/// <param name="newVolume">returns back the current setting for confirmation</param>
/// <returns>The current set volume</returns>
public float SetVolume(float newVolume)
{
try
{
if (newVolume > 1 || newVolume < 0) return GetVolume();
if (sourceVoice == null || sourceVoice.IsDisposed)
{
CurrentVolume = newVolume;
return newVolume;
}
sourceVoice.SetVolume(newVolume, 0);
return GetVolume();
}
catch (Exception e)
{
LastErrorMsg = "SetVolume(): " + e.Message;
return 0.0f;
}
}
/// <summary>
/// End of buffer event handler
/// </summary>
/// <param name="obj"></param>
private void SourceVoice_BufferEnd(IntPtr obj)
{
//Debug.WriteLine($"buffer end reached with looping {IsLooping}");
if (!IsLooping)
{
if (IsPlaying && !IsUserStop)
Playing.Set();
else if(IsUserStop)
{
IsUserStop = false;
}
}
}
public void Dispose()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
~SoundServices()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
}
And you can use multiple SoundServices instances as you need:
var sp = new SoundServices(new MemoryStream(File.ReadAllBytes(WavFileName)), true);
if (sp != null)
sp.PlaySound();
To keep the intro short, I will mention that I have been building an application that involves executing remote commands from a private website that I have built and having my personal home computer respond to those commands.
I discovered that live desktop-streaming would be a perfect feature, and I am planning to use an iframe to fit it into my site. However, I cannot seem to find a good C# library which will allow me to stream my desktop in realtime.
Other than this one: http://www.codeproject.com/Articles/371955/Motion-JPEG-Streaming-Server
The problem is, that only allows me to stream my desktop to localhost, 127.0.0.1, and the other local host links.
I need a way to modify it to be able to have it stream to a server of my choice, from which I can then access it. For example www.mystream.com/stream.php
It consists of two classes: ImageStreamingServer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using System.IO;
// -------------------------------------------------
// Developed By : Ragheed Al-Tayeb
// e-Mail : ragheedemail#gmail.com
// Date : April 2012
// -------------------------------------------------
namespace rtaNetworking.Streaming
{
/// <summary>
/// Provides a streaming server that can be used to stream any images source
/// to any client.
/// </summary>
public class ImageStreamingServer:IDisposable
{
private List<Socket> _Clients;
private Thread _Thread;
public ImageStreamingServer():this(Screen.Snapshots(600,450,true))
{
}
public ImageStreamingServer(IEnumerable<Image> imagesSource)
{
_Clients = new List<Socket>();
_Thread = null;
this.ImagesSource = imagesSource;
this.Interval = 50;
}
/// <summary>
/// Gets or sets the source of images that will be streamed to the
/// any connected client.
/// </summary>
public IEnumerable<Image> ImagesSource { get; set; }
/// <summary>
/// Gets or sets the interval in milliseconds (or the delay time) between
/// the each image and the other of the stream (the default is .
/// </summary>
public int Interval { get; set; }
/// <summary>
/// Gets a collection of client sockets.
/// </summary>
public IEnumerable<Socket> Clients { get { return _Clients; } }
/// <summary>
/// Returns the status of the server. True means the server is currently
/// running and ready to serve any client requests.
/// </summary>
public bool IsRunning { get { return (_Thread != null && _Thread.IsAlive); } }
/// <summary>
/// Starts the server to accepts any new connections on the specified port.
/// </summary>
/// <param name="port"></param>
public void Start(int port)
{
lock (this)
{
_Thread = new Thread(new ParameterizedThreadStart(ServerThread));
_Thread.IsBackground = true;
_Thread.Start(port);
}
}
/// <summary>
/// Starts the server to accepts any new connections on the default port (8080).
/// </summary>
public void Start()
{
this.Start(8080);
}
public void Stop()
{
if (this.IsRunning)
{
try
{
_Thread.Join();
_Thread.Abort();
}
finally
{
lock (_Clients)
{
foreach (var s in _Clients)
{
try
{
s.Close();
}
catch { }
}
_Clients.Clear();
}
_Thread = null;
}
}
}
/// <summary>
/// This the main thread of the server that serves all the new
/// connections from clients.
/// </summary>
/// <param name="state"></param>
private void ServerThread(object state)
{
try
{
Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Server.Bind(new IPEndPoint(IPAddress.Any,(int)state));
Server.Listen(10);
System.Diagnostics.Debug.WriteLine(string.Format("Server started on port {0}.", state));
foreach (Socket client in Server.IncommingConnectoins())
ThreadPool.QueueUserWorkItem(new WaitCallback(ClientThread), client);
}
catch { }
this.Stop();
}
/// <summary>
/// Each client connection will be served by this thread.
/// </summary>
/// <param name="client"></param>
private void ClientThread(object client)
{
Socket socket = (Socket)client;
System.Diagnostics.Debug.WriteLine(string.Format("New client from {0}",socket.RemoteEndPoint.ToString()));
lock (_Clients)
_Clients.Add(socket);
try
{
using (MjpegWriter wr = new MjpegWriter(new NetworkStream(socket, true)))
{
// Writes the response header to the client.
wr.WriteHeader();
// Streams the images from the source to the client.
foreach (var imgStream in Screen.Streams(this.ImagesSource))
{
if (this.Interval > 0)
Thread.Sleep(this.Interval);
wr.Write(imgStream);
}
}
}
catch { }
finally
{
lock (_Clients)
_Clients.Remove(socket);
}
}
#region IDisposable Members
public void Dispose()
{
this.Stop();
}
#endregion
}
static class SocketExtensions
{
public static IEnumerable<Socket> IncommingConnectoins(this Socket server)
{
while(true)
yield return server.Accept();
}
}
static class Screen
{
public static IEnumerable<Image> Snapshots()
{
return Screen.Snapshots(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,true);
}
/// <summary>
/// Returns a
/// </summary>
/// <param name="delayTime"></param>
/// <returns></returns>
public static IEnumerable<Image> Snapshots(int width,int height,bool showCursor)
{
Size size = new Size(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
Bitmap srcImage = new Bitmap(size.Width, size.Height);
Graphics srcGraphics = Graphics.FromImage(srcImage);
bool scaled = (width != size.Width || height != size.Height);
Bitmap dstImage = srcImage;
Graphics dstGraphics = srcGraphics;
if(scaled)
{
dstImage = new Bitmap(width, height);
dstGraphics = Graphics.FromImage(dstImage);
}
Rectangle src = new Rectangle(0, 0, size.Width, size.Height);
Rectangle dst = new Rectangle(0, 0, width, height);
Size curSize = new Size(32, 32);
while (true)
{
srcGraphics.CopyFromScreen(0, 0, 0, 0, size);
if (showCursor)
Cursors.Default.Draw(srcGraphics,new Rectangle(Cursor.Position,curSize));
if (scaled)
dstGraphics.DrawImage(srcImage, dst, src, GraphicsUnit.Pixel);
yield return dstImage;
}
srcGraphics.Dispose();
dstGraphics.Dispose();
srcImage.Dispose();
dstImage.Dispose();
yield break;
}
internal static IEnumerable<MemoryStream> Streams(this IEnumerable<Image> source)
{
MemoryStream ms = new MemoryStream();
foreach (var img in source)
{
ms.SetLength(0);
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
yield return ms;
}
ms.Close();
ms = null;
yield break;
}
}
}
MjpegWriter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
// -------------------------------------------------
// Developed By : Ragheed Al-Tayeb
// e-Mail : ragheedemail#gmail.com
// Date : April 2012
// -------------------------------------------------
namespace rtaNetworking.Streaming
{
/// <summary>
/// Provides a stream writer that can be used to write images as MJPEG
/// or (Motion JPEG) to any stream.
/// </summary>
public class MjpegWriter:IDisposable
{
private static byte[] CRLF = new byte[] { 13, 10 };
private static byte[] EmptyLine = new byte[] { 13, 10, 13, 10};
private string _Boundary;
public MjpegWriter(Stream stream)
: this(stream, "--boundary")
{
}
public MjpegWriter(Stream stream,string boundary)
{
this.Stream = stream;
this.Boundary = boundary;
}
public string Boundary { get; private set; }
public Stream Stream { get; private set; }
public void WriteHeader()
{
Write(
"HTTP/1.1 200 OK\r\n" +
"Content-Type: multipart/x-mixed-replace; boundary=" +
this.Boundary +
"\r\n"
);
this.Stream.Flush();
}
public void Write(Image image)
{
MemoryStream ms = BytesOf(image);
this.Write(ms);
}
public void Write(MemoryStream imageStream)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(this.Boundary);
sb.AppendLine("Content-Type: image/jpeg");
sb.AppendLine("Content-Length: " + imageStream.Length.ToString());
sb.AppendLine();
Write(sb.ToString());
imageStream.WriteTo(this.Stream);
Write("\r\n");
this.Stream.Flush();
}
private void Write(byte[] data)
{
this.Stream.Write(data, 0, data.Length);
}
private void Write(string text)
{
byte[] data = BytesOf(text);
this.Stream.Write(data, 0, data.Length);
}
private static byte[] BytesOf(string text)
{
return Encoding.ASCII.GetBytes(text);
}
private static MemoryStream BytesOf(Image image)
{
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms;
}
public string ReadRequest(int length)
{
byte[] data = new byte[length];
int count = this.Stream.Read(data,0,data.Length);
if (count != 0)
return Encoding.ASCII.GetString(data, 0, count);
return null;
}
#region IDisposable Members
public void Dispose()
{
try
{
if (this.Stream != null)
this.Stream.Dispose();
}
finally
{
this.Stream = null;
}
}
#endregion
}
}
This application should work over internet as well. Make sure that the port you access through it remotely is open.
If you don't want to have to open a port for the app to work. then consider changing the source above to make a reverse connection instead , where your desktop computer acts as a client rather than a server.
Good luck
My application has to writing received UDP data to disk continuously, and the speed is about 40M/s - 50M/s. My current logic is that all data is cached in a queue and Create a BinaryWriter to write queue data to local disk. It works well most time, but sometimes the writing speed will be very slow and causes cached data too large, then my application crushes.
Is there any method to improve writing file speed and keep a stable writing speed? Or i have to adjust my hardware?
Add more info:
The hareware could sustain my application write, I ran application for 1 hours many times, application only crushes occasionally.
This is my class to write file:
class BufferedFileStorageManager : IFileStorageManager
{
private BinaryWriter writer;
private ConcurrentQueue<byte[]> buffer;
private bool isWriting;
private bool isClosed;
public void Open(string filePath)
{
buffer = new ConcurrentQueue<byte[]>();
writer = new BinaryWriter(new FileStream(filePath, FileMode.Create));
isWriting = false;
isClosed = false;
}
/// <summary>
/// Writes the specified data syncronizly. Method will return after writing finished.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte[] data)
{
writer.Write(data);
}
/// <summary>
/// Write the data asyncronizly.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public async Task WriteAsync(byte[] data)
{
if (isClosed)
{
var closedTask = Task.Factory.StartNew(() => LogManager.Warn("File is closed, skip writing data."));
await closedTask;
}
else
{
buffer.Enqueue(data);
var writeTask = Task.Factory.StartNew(WriteInQueue);
await writeTask;
}
}
private void WriteInQueue()
{
if (isWriting)
return;
isWriting = true;
byte[] data;
while (buffer.TryDequeue(out data))
{
writer.Write(data);
}
isWriting = false;
}
/// <summary>
/// Close file. Method will return until all pending data is written to file
/// </summary>
public void Close()
{
WriteInQueue();
while (isWriting)
{
Thread.Sleep(1);
}
writer.Close();
}
public async Task CloseAsync()
{
isClosed = true;
var closeTask = new Task(Close);
closeTask.Start();
await closeTask;
writer.Dispose();
writer = null;
}
}
I have legacy code which performs some very long operations on the UI thread.
What I want to do is to show a progress bar with message and run the work on another thread. Unfortunately , for now I don't have access to VS2012 so I can't use the async keyword.
I've written some code which works fine with operations of 0-1 parameters and no return value using Action.
But when I tried adjusting it to support Func I encountered some issues with
invoking the tasks and returning TResult.
Attached is my original code, would appreciate any suggestions. Thanks, Omer
public partial class FreeProgressBarFrm : System.Windows.Forms.Form
{
#region Members
/// <summary>
/// timer for the progress bar.
/// </summary>
private Timer m_Timer = new Timer();
/// <summary>
/// Delegate for the background operation to perform.
/// </summary>
private Action m_backgroundOperation;
/// <summary>
/// Standard operation to show the user while the operation is in progress.
/// </summary>
private static readonly string m_performingUpdatesMessage = IO_Global.GetResourceString("Performing updates, please wait", "Performing updates, please wait", null);
#endregion
#region Constructor
/// <summary>
/// Constructor
/// </summary>
/// <param name="backgroundDelegate"> Delegate for the background operation to perform</param>
/// <param name="operationName">meessage to show the user while the operation is in progress.</param>
public FreeProgressBarFrm(Action backgroundDelegate, string operationName)
{
InitializeComponent();
m_backgroundOperation = backgroundDelegate;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.lblOperation.Text = operationName;
m_Timer.Interval = 1000;
m_Timer.Tick += new EventHandler(m_Timer_Tick);
}
/// <summary>
/// Constructor , for progressbar with defalt user message (performing updates, please wait).
/// </summary>
/// <param name="backgroundDelegate"> Delegate for the background operation to perform</param>
/// <param name="operationName">operation display name</param>
public FreeProgressBarFrm(Action backgroundDelegate): this(backgroundDelegate, m_performingUpdatesMessage)
{
}
#endregion
#region Methods
/// <summary>
/// Call this method to begin backgorund operation while
/// showing the progress bar to the client.
/// </summary>
public void Wait()
{
ShowDialog(ControlsHelper.MainFrm);
}
/// <summary>
/// Advance the progress bar
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void m_Timer_Tick(object sender, EventArgs e)
{
PerformStep();
}
/// <summary>
/// Advance the progress bar
/// </summary>
private void PerformStep()
{
this.progressBar1.PerformStep();
this.lblOperation.Refresh();
if (this.progressBar1.Value == this.progressBar1.Maximum)
{
this.progressBar1.Value = this.progressBar1.Minimum;
}
}
/// <summary>
/// Load the form , start the progress bar and backroud task.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ProgressBarFrm_Load(object sender, EventArgs e)
{
m_Timer.Start();
this.lblOperation.Refresh();
Task task = new Task(m_backgroundOperation);
Task UITask = task.ContinueWith(delegate { OnWorkCompleted(); },
TaskScheduler.FromCurrentSynchronizationContext());
try
{
task.Start();
}
catch (Exception)
{
Close();
throw;
}
}
/// <summary>
/// Called when the work has been completed.
/// </summary>
private void OnWorkCompleted()
{
Close();
}
/// <summary>
/// Close the timer.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ProgressBarFrm_FormClosing(object sender, FormClosingEventArgs e)
{
if (m_Timer != null)
{
m_Timer.Dispose();
m_Timer = null;
}
}
#endregion
}
Here is the way i'm executing some async work,
public static class TaskExecuter
{
private static readonly ThreadLocal<List<BackgroundTask>> TasksToExecute =
new ThreadLocal<List<BackgroundTask>>(() => new List<BackgroundTask>());
public static Action<Exception> ExceptionHandler { get; set; }
public static void ExecuteLater(BackgroundTask task)
{
TasksToExecute.Value.Add(task);
}
public static void Discard()
{
TasksToExecute.Value.Clear();
}
public static void StartExecuting()
{
var value = TasksToExecute.Value;
var copy = value.ToArray();
value.Clear();
if (copy.Length > 0)
{
Task.Factory.StartNew(() =>
{
foreach (var backgroundTask in copy)
ExecuteTask(backgroundTask);
}, TaskCreationOptions.LongRunning)
.ContinueWith(task =>
{
if (ExceptionHandler != null)
ExceptionHandler(task.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
public static void ExecuteTask(BackgroundTask task)
{
task.Run();
}
}
Here is the base class
public abstract class BackgroundTask
{
protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
protected virtual void Initialize()
{
}
protected virtual void OnError(Exception e)
{
//do some work
}
public bool? Run()
{
Logger.Info("Started task: {0}", GetType().Name);
Initialize();
try
{
Execute();
TaskExecuter.StartExecuting();
return true;
}
catch (Exception e)
{
Logger.ErrorException("Could not execute task " + GetType().Name, e);
OnError(e);
return false;
}
finally
{
TaskExecuter.Discard();
Logger.Info("Finished task: {0}", GetType().Name);
}
}
public abstract void Execute();
}
Here is the example of using
public class SendEmailTask : BackgroundTask
{
private const string MailServerIp = "yourip";
public string[] To { get; set; }
public string From { get; set; }
public string Template { get; set; }
public object ViewContext { get; set; }
public string[] Attachments { get; set; }
public string Subject { get; set; }
public override void Execute()
{
MailMessage message = new MailMessage();
try
{
MailAddress mailAddress = new MailAddress(From);
message.From = mailAddress;
foreach (string to in To) message.To.Add(to);
message.Subject = Subject;
if (Attachments.ReturnSuccess())
{
foreach (string attachment in Attachments)
message.Attachments.Add(new Attachment(attachment));
}
message.Priority = MailPriority.High;
message.Body = Template;
message.AlternateViews
.Add(AlternateView
.CreateAlternateViewFromString(ViewContext.ToString(), new ContentType("text/html")));
message.IsBodyHtml = true;
new SmtpClient(MailServerIp)
{
Port = 25,
UseDefaultCredentials = true
}.Send(message);
}
catch (Exception e)
{
Logger.FatalException("Error sending email:", e);
}
finally
{
message.Dispose();
}
}
public override string ToString()
{
return string.Format("To: {0}, From: {1}, Template: {2}, ViewContext: {3}, Attachments: {4}, Subject: {5}", To, From, Template, ViewContext, Attachments, Subject);
}
}
Here i've added a changed version for your needs
public static class AsyncExecuter
{
private static readonly ThreadLocal<List<Action>> TasksToExecute =
new ThreadLocal<List<Action>>(() => new List<BackgroundTask>());
public static Action<Exception> ExceptionHandler { get; set; }
public static void ExecuteLater(BackgroundTask task)
{
TasksToExecute.Value.Add(task);
}
public static void Discard()
{
TasksToExecute.Value.Clear();
}
public static void StartExecuting()
{
var value = TasksToExecute.Value;
var copy = value.ToArray();
value.Clear();
if (copy.Length > 0)
{
Task.Factory.StartNew(() =>
{
foreach (var backgroundTask in copy)
ExecuteTask(backgroundTask);
}, TaskCreationOptions.LongRunning)
.ContinueWith(task =>
{
if (ExceptionHandler != null)
ExceptionHandler(task.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
public static void ExecuteTask(Action task)
{
task.Invoke();
}
}
I'm developing a thread safe class that I'll use as cache, it should work in .NET and Mono.
The items have a time to live, and every time that a object is retrieved, its time to live is refreshed. Each time that I add a item, the timestamp is added to another collection that holds the same keys. A timer raises the method that looks for out dated items and remove them.
When I try to get and item, I have to provide also a delegate indicating how to obtain it if it wouldn't exist in the cache.
I've testing, and although the items removal should happen every 30 seconds in the test, it's happening very often, almost every second, and I'don't know why.
This is the class:
public class GenericCache<TId, TItem>:IDisposable where TItem : class
{
SortedDictionary<TId, TItem> _cache;
SortedDictionary<TId, DateTime> _timeouts;
Timer _timer;
Int32 _cacheTimeout;
System.Threading.ReaderWriterLockSlim _locker;
public GenericCache(Int32 minutesTTL)
{
_locker = new System.Threading.ReaderWriterLockSlim();
_cacheTimeout = minutesTTL;
_cache = new SortedDictionary<TId, TItem>();
_timeouts = new SortedDictionary<TId, DateTime>();
_timer = new Timer((minutesTTL * 60) / 2);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Start();
}
/// <summary>
/// Get an item, if it doesn't exist, create it using the delegate
/// </summary>
/// <param name="id">Id for the item</param>
/// <param name="create">A delegate that generates the item</param>
/// <returns>The item</returns>
public TItem Get(TId id, Func<TItem> create)
{
_locker.EnterUpgradeableReadLock();
try
{
TItem item = _cache.Where(ci => ci.Key.Equals(id)).Select(ci => ci.Value).SingleOrDefault();
if (item == null)
{
_locker.EnterWriteLock();
// check again, maybe another thread is waiting in EnterWriteLock cos the same item is null
item = _cache.Where(ci => ci.Key.Equals(id)).Select(ci => ci.Value).SingleOrDefault();
if (item == null)
{
Debug.Write("_");
item = create.Invoke();
if (item != null)
{
_cache.Add(id, item);
_timeouts.Add(id, DateTime.Now);
}
}
}
else
_timeouts[id] = DateTime.Now;
return item;
}
finally
{
if(_locker.IsWriteLockHeld)
_locker.ExitWriteLock();
_locker.ExitUpgradeableReadLock();
}
}
/// <summary>
/// Execute a delegate in the items, for example clear nested collections.
/// </summary>
/// <param name="action">The delegate</param>
public void ExecuteOnItems(Action<TItem> action)
{
_locker.EnterWriteLock();
try
{
foreach (var i in _cache.Values)
action.Invoke(i);
}
finally
{
_locker.ExitWriteLock();
}
}
/// <summary>
/// Clear this cache
/// </summary>
public void Clear()
{
_locker.EnterWriteLock();
try
{
_cache.Clear();
_timeouts.Clear();
}
finally
{
_locker.ExitWriteLock();
}
}
/// <summary>
/// Remove outdated items
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
_locker.EnterUpgradeableReadLock();
try
{
var delete = _timeouts.Where(to => DateTime.Now.Subtract(to.Value).TotalMinutes > _cacheTimeout).ToArray();
if(delete.Any())
{
_locker.EnterWriteLock();
foreach (var timeitem in delete)
{
Debug.Write("-");
_cache.Remove(timeitem.Key);
_timeouts.Remove(timeitem.Key);
}
}
}
finally
{
if(_locker.IsWriteLockHeld)
_locker.ExitWriteLock();
_locker.ExitUpgradeableReadLock();
}
}
#region IDisposable Members
private volatile Boolean disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
try
{
this.Clear();
}
finally
{
_locker.Dispose();
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~GenericCache()
{
Dispose(false);
}
#endregion
}
As you can see, in debug mode, when a item is added a "_" symbol is printed, and when and item is removed a "-" symbol is printed. In the tests, after the second minute can see how items are removed and added in the same second, when the items should be removed only every 30 seconds, and I don't know why:
This is how I tests:
static void Main(string[] args)
{
GenericCache<Int32, String> cache = new GenericCache<Int32, String>(1);
Debug.Listeners.Add(new ConsoleTraceListener());
Action a = delegate()
{
Random r = new Random(DateTime.Now.Millisecond);
while (true)
{
Int32 number = r.Next(0, 9999);
if (String.IsNullOrEmpty(cache.Get(number, () => number.ToString())))
Debug.Write("E");
Thread.Sleep(number);
}
};
for (int i = 0; i < 150; i++)
{
new Thread(new ThreadStart(a)).Start();
Thread.Sleep(5);
}
Console.ReadKey();
}
Do you see any problem in the GenericCache class?
Thanks in advance, kind regards.
First issue i see (assuming you are using System.Timers.Timer accepts milliseconds and you are passing seconds).
_timer = new Timer((minutesTTL * 60000) / 2);