Медленная работа и ловля исключений в многопоточной программе - 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;
- }
- .....
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д