Медленная работа и ловля исключений в многопоточной программе - 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;
            }
            .....

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

14   голосов , оценка 4 из 5
Похожие ответы