Медленная работа и ловля исключений в многопоточной программе - C#
Формулировка задачи:
Здравствуйте. Сразу хочу предупредить, что это мой первый проект поэтому судите строго, но не тролльте )
В общем после прочтения нескольких книг, естественно не до конца. Захотелось по быстрее написать что-то рабочее. И недолго думая решил написать многопоточный файл менеджер.
И практически сразу наткнулся на две проблемы.
1-ая: Как поймать исключение дочерних потоков в основном потоке ?
2-ая: Почему загрузка файла и сохранение его на диск происходит довольно длительное время?
Код либы в которой я качаю и сохраняю файл:
Весь проект целиком
using System; using System.Net; using System.Net.Http.Headers; using System.Collections.Generic; using System.Threading; using System.Net.Mime; using System.IO; namespace phttp { public class HttpClient { protected readonly HttpConfigurator config; protected readonly Uri uri; protected Queue<HttpClientRange> queue; protected List<HttpClientRange> ranges; protected UInt32 threadsRun = 0; protected UInt32 threadsFinished = 0; protected string fileName; protected string fullFileName; protected string charSet; protected long bytesRecieved = 0; protected long bytesRecievedTotal = 0; protected long contentLength; static object locker = new object(); static object filelocker = new object(); public HttpClient(HttpConfigurator config) { this.config = config; if (isValidURL(config.source)) this.uri = new Uri(config.source); } public void Run() { Thread t = new Thread(_Run); t.SetApartmentState(ApartmentState.STA); t.Start(); } protected void _Run() { if (ranges != null) { ranges = null; } WriteLog("Get headers"); WebResponse headResponse = getHeadData(config.source, config.method); UInt32 threadCount = 1; if (headResponse.Headers.Get("Accept-Ranges") != "") { threadCount = config.threadCount; } WriteLog("Check path to file"); fileName = headResponse.ResponseUri.Segments[headResponse.ResponseUri.Segments.Length - 1].Trim(); var contentDispositionString = headResponse.Headers.Get("Content-Disposition"); if (contentDispositionString != null && contentDispositionString.Length > 0) { var contentDisposition = ContentDispositionHeaderValue.Parse(contentDispositionString); if (contentDisposition.FileName.Length > 0) { fileName = contentDisposition.FileName; } } var contentTypeString = headResponse.Headers.Get("Content-Type"); if (contentTypeString != null && contentTypeString.Length > 0) { var ContentType = new ContentType(contentTypeString); if (ContentType.MediaType == "text/html") { fileName = Common.Fs.ChangeExtension(fileName, ".html"); } charSet = ContentType.CharSet; } if (fileName == null || fileName.Length == 0) { throw new HttpClientException(string.Format("Program cant get filename for {0}", config.source)); } if (config.savePath == null || config.savePath.Length > 0) { fullFileName = Common.Fs.CreateFullFileName(config.savePath, fileName); } contentLength = Convert.ToUInt32(headResponse.Headers.Get("Content-Length")); var contentRangeString = headResponse.Headers.Get("Content-Range"); if (contentRangeString != null && contentRangeString != "") { var crhv = ContentRangeHeaderValue.Parse(contentRangeString); contentLength = (long) crhv.Length; } // make segmentation WriteLog("Make segmentation download data"); UInt32 iterSegment = 0; queue = new Queue<HttpClientRange>(); if (threadCount == 1) { var hcr = new HttpClientRange(); hcr.id = 1; hcr.start = hcr.stop = hcr.bytes = 0; queue.Enqueue(hcr); } else { long start = 0; long stop = contentLength; do { iterSegment++; if (start > 0) start += 1; var htr = new HttpClientRange(); htr.start = start; htr.stop = start + config.segmentBytes; if (htr.stop > stop) htr.stop = stop; start = htr.stop; htr.bytes = config.segmentBytes; htr.id = iterSegment; queue.Enqueue(htr); } while (start < stop); } ranges = new List<HttpClientRange>(); do { if (threadsRun >= threadCount) { Thread.Sleep(250); continue; } HttpClientRange segment = queue.Dequeue(); Thread t = new Thread(new ParameterizedThreadStart(Download)); t.SetApartmentState(ApartmentState.STA); segment.thread = t; segment.setState(HttpClientRange.STATE_START); ranges.Add(segment); t.Start(segment); lock (locker) { threadsRun++; } } while (queue.Count > 0); } protected void Download(object o) { HttpClientRange hcr = o as HttpClientRange; WriteLog(string.Format("Start thread [{0}]", hcr.id)); hcr.setState(HttpClientRange.STATE_START); HttpWebRequest wr = HttpWebRequest.CreateHttp(uri); if (hcr.stop > 0) { wr.AddRange((int) hcr.start, (int) hcr.stop); } wr.Method = config.method; WebResponse response = wr.GetResponse(); Stream ReceiveStream = response.GetResponseStream(); byte[] buffer = new byte[1024]; int bytesRead; long pos = hcr.start; while ((bytesRead = ReceiveStream.Read(buffer, 0, buffer.Length)) != 0) { write(pos, buffer, bytesRead); pos += bytesRead; } response.Close(); WriteLog(string.Format("Stop thread [{0}]", hcr.id)); hcr.setState(HttpClientRange.STATE_FINISH); lock (locker) { threadsRun--; } } public static bool isValidURL(string source) { Uri uriResult; return Uri.TryCreate(source, UriKind.Absolute, out uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); } public WebResponse getHeadData(string source, string method) { if (!isValidURL(source)) throw new HttpClientException("URL is incorrect"); HttpWebRequest wr = HttpWebRequest.CreateHttp(source); wr.Method = method; wr.AddRange(0,1); var wres = wr.GetResponse(); wr.Abort(); wres.Close(); return wres; } public void WriteLog(string message) { if (config.logManager != null) { config.logManager.AddLogText(message); } } protected void write(long pos, byte[] buffer, int bytesRead) { lock (filelocker) { FileStream outFile = new FileStream(fullFileName, FileMode.OpenOrCreate); outFile.Seek(pos, SeekOrigin.Begin); outFile.Write(buffer, 0, bytesRead); outFile.Close(); } lock (locker) { bytesRecieved += bytesRead; } Progress(); } protected void Progress() { if (config.progress != null) { config.progress((int)(100*bytesRecieved /contentLength)); } } } public class HttpClientRange { public static readonly string STATE_NEW = "new"; public static readonly string STATE_START = "start"; public static readonly string STATE_SUSPEND = "suspend"; public static readonly string STATE_FINISH = "finish"; public UInt32 id; public long bytes; public long start; public long stop; public readonly Guid guid; public Thread thread; public WebClient wc; protected string _state; public string state { get { return _state; } } public HttpClientRange() { _state = STATE_NEW; guid = Guid.NewGuid(); } public void setState(string state) { List<string> list = new List<string>(); list.Add(STATE_NEW); list.Add(STATE_START); list.Add(STATE_SUSPEND); list.Add(STATE_FINISH); foreach (var item in list) { if (item == state) { _state = state; return; } } throw new HttpClientRangeException(string.Format("State {0} is incorrect", state)); } } public class HttpClientException : Exception { public HttpClientException(string message) : base(message) { } } public class HttpClientRangeException : Exception { public HttpClientRangeException(string message) : base(message) { } } }
using System; using System.Collections.Generic; using Loger; namespace phttp { public delegate void Progress(int step); public class HttpConfigurator { public readonly string source; public readonly string savePath; public readonly UInt32 threadCount; public readonly long segmentBytes; public bool writeToFile = true; public string charSet; public LogManager logManager; public Progress progress; private Dictionary<string, string> methods; private string _method = "GET"; public string method { set { if (methods.ContainsKey(value)) _method = value; } get { return _method; } } public HttpConfigurator(string source, string savePath, UInt32 threadCount, long segmentBytes) { this.source = source; this.savePath = savePath; this.threadCount = threadCount; this.segmentBytes = segmentBytes; methods = new Dictionary<string, string>(); methods.Add("GET", "GET"); methods.Add("POST", "POST"); } } }
Решение задачи: «Медленная работа и ловля исключений в многопоточной программе»
textual
Листинг программы
public event ErrorEventHandler ErrorAppeared; public void Run() { Thread t = new Thread(_Run); t.SetApartmentState(ApartmentState.STA); t.Start(); } protected void _Run() { try { __Run(); } catch (Exception ex) { if (ErrorAppeared != null) ErrorAppeared(null, new ErrorEventArgs(ex)); } } protected void __Run() { if (ranges != null) { ranges = null; } .....
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д