5天不再惧怕多线程——第一天 尝试Thread

简介:

     原本准备在mongodb之后写一个lucene.net系列,不过这几天用到多线程时才发现自己对多线程的了解少之又少,仅仅停留在lock上面,

故这几天看了下线程参考手册结合自己的心得整理一下放在博客上作为自己的学习笔记。

     好了,我们知道“负载”是一个很时尚,很牛X的玩意,往大处说,网站需要负载,数据库需要负载。往小处说,线程也需要负载,面对海量的

用户请求,我们的单线程肯定扛不住,那么怎么办,一定要负载,所以说多线程是我们码农必须要熟练掌握的一门技术。

    在framework中给我们提供了一个Threading命名空间,下面是一个msdn上不完整的截图:

在后面的系列中我也是主要整理这几个类的使用方法和应用场景。

一:Thread的使用

      我们知道这个类代表处理器线程,在Thread中有几个比较常用和重要的方法。

<1> sleep:  这个算是最简单的了。

<2> join:    这个可以让并发行处理变成串行化,什么意思呢?上代码说话最清楚。

class Test
{
    static void Main()
    {
        Thread t = new Thread(Run);

        t.Start();

        //Join相当于把Run方法内嵌如此
        t.Join();

        //该死的t.Join(),害的我主线程必须在你执行完后才能执行。
        Console.WriteLine("我是主线程:" + Thread.CurrentThread.GetHashCode());
    }

    static void Run()
    {
        //等待5s
        Thread.Sleep(5000);

        Console.WriteLine("我是线程:" + Thread.CurrentThread.GetHashCode());
    }
}

<3> Interrupt和Abort:这两个关键字都是用来强制终止线程,不过两者还是有区别的。

        ① Interrupt:  抛出的是 ThreadInterruptedException 异常。

                 Abort:  抛出的是  ThreadAbortException 异常。

        ② Interrupt:如果终止工作线程,只能管到一次,工作线程的下一次sleep就管不到了,相当于一个

                             contine操作。

                 Abort:这个就是相当于一个break操作,工作线程彻底死掉。 

 

Interrupt:

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(new ThreadStart(Run));

            t.Start();

            //阻止动作
            t.Interrupt();

            Console.Read();
        }

        static void Run()
        {
            for (int i = 1; i <= 3; i++)
            {
                Stopwatch watch = new Stopwatch();

                try
                {
                    watch.Start();
                    Thread.Sleep(2000);
                    watch.Stop();

                    Console.WriteLine("第{0}延迟执行:{1}ms", i, watch.ElapsedMilliseconds);
                }
                catch (ThreadInterruptedException e)
                {
                    Console.WriteLine("第{0}延迟执行:{1}ms,不过抛出异常", i, watch.ElapsedMilliseconds);
                }
            }
        }
    }
}

 

Abort:   工作线程直接退出,不带走一片云彩。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(new ThreadStart(Run));

            t.Start();

            Thread.Sleep(100);

            //阻止动作
            t.Abort();

            Console.Read();
        }

        static void Run()
        {
            for (int i = 1; i <= 3; i++)
            {
                Stopwatch watch = new Stopwatch();

                try
                {
                    watch.Start();
                    Thread.Sleep(2000);
                    watch.Stop();

                    Console.WriteLine("第{0}延迟执行:{1}ms", i, watch.ElapsedMilliseconds);
                }
                catch (ThreadAbortException e)
                {
                    Console.WriteLine("第{0}延迟执行:{1}ms,不过抛出异常", i, watch.ElapsedMilliseconds);
                }
            }
        }
    }
}

 


二:线程使用场景

    可能线程的使用有点类似wcf,做一些耗时但不很及时的需求,比如可以开线程下图片,连接数据库等等,当然线程可以用来做负载,这里就做

一个小demo,找一个美女网站,面对如此多的图片,一个线程真的吃不消啊,

看了下网站主体上有4个tab页,那么我们就开4个线程来负载

class Program
    {
        static void Main(string[] args)
        {
            string[] str = { "model", "sexy", "belle", "stars" };

            for (int url = 0; url < str.Length; url++)
            {
                Thread thread = new Thread(DownLoad);

                thread.Start(str[url]);
            }
            Console.Read();
        }

        public static void DownLoad(object category)
        {
            string url = string.Empty;

            for (int purl = 9014; purl > 10; purl--)
            {
                for (int pageSize = 0; pageSize < 20; pageSize++)
                {
                    try
                    {
                        if (pageSize == 0)
                            url = "http://www.mm8mm8.com/" + category + "/" + purl + ".html";
                        else
                            url = "http://www.mm8mm8.com/" + category + "/" + purl + "_" + pageSize + ".html";

                        //创建http链接
                        var request = (HttpWebRequest)WebRequest.Create(url);

                        request.Timeout = 1000 * 5;    //5s过期

                        var response = (HttpWebResponse)request.GetResponse();

                        Stream stream = response.GetResponseStream();

                        StreamReader sr = new StreamReader(stream);

                        string content = sr.ReadToEnd();

                        var list = GetHtmlImageUrlList(content);

                        WebClient client = new WebClient();

                        string[] directory = { @"C:\MM\", @"D:\MM\", @"E:\MM\", @"F:\MM\" };

                        var directoryName = directory[new Random().Next(0, directory.Length)];

                        if (!Directory.Exists(directoryName))
                            Directory.CreateDirectory(directoryName);

                        var fileName = string.Empty;

                        if (list.Count == 0)
                        {
                            Console.WriteLine("时间:" + DateTime.Now + " 当前网址:" + url + "  未发现图片");
                            break;
                        }

                        try
                        {

                            fileName = category + "_" + purl + "_" + (pageSize + 1) + ".jpg";

                            var localFile = directoryName + fileName;

                            var imageRequest = (HttpWebRequest)WebRequest.Create(list[0]);

                            imageRequest.Timeout = 1000 * 5;  //5s 超时

                            var imageResponse = (HttpWebResponse)imageRequest.GetResponse();

                            var s = imageResponse.GetResponseStream();

                            Image image = Image.FromStream(s);

                            image.Save(localFile);

                            image.Dispose();

                            Console.WriteLine("时间:" + DateTime.Now + "  图片:" + fileName + " 已经下载   存入磁盘位置:" + localFile);

                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("时间:" + DateTime.Now + " 当前图片:" + fileName + " 错误信息:" + e.Message);
                            continue;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("时间:" + DateTime.Now + " 当前网址:" + url + " 错误信息:" + ex.Message);
                    }
                }
            }
        }

        /// <summary> 
/// 取得HTML中所有图片的 URL。 
/// </summary> 
/// <param name="sHtmlText">HTML代码</param> 
/// <returns>图片的URL列表</returns> 
        public static List<string> GetHtmlImageUrlList(string sHtmlText)
        {
            // 定义正则表达式用来匹配 img 标签 
            Regex regImg = new Regex(@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>", RegexOptions.IgnoreCase);

            // 搜索匹配的字符串 
            MatchCollection matches = regImg.Matches(sHtmlText);

            List<string> sUrlList = new List<string>();

            // 取得匹配项列表 
            foreach (Match match in matches)
                sUrlList.Add(match.Groups["imgUrl"].Value);
            return sUrlList;
        }
    }


三:对线程的一些思考

    我们知道线程的优点还是比较多的,每个线程都需要默认的堆栈空间,所以说线程数受到内存空间大小的限制,如果线程数开的太多

反而适得其反,进程被分配的时间片会被线程分的更细,也就导致了处理器需要更频繁的在线程之间来回切换。

相关文章
|
4月前
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
54 0
|
3月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
57 7
|
14天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
13 3
|
14天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
27 2
|
14天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
27 2
|
14天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
26 1
|
2月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
【多线程面试题 二】、 说说Thread类的常用方法
Thread类的常用方法包括构造方法(如Thread()、Thread(Runnable target)等)、静态方法(如currentThread()、sleep(long millis)、yield()等)和实例方法(如getId()、getName()、interrupt()、join()等),用于线程的创建、控制和管理。
|
4月前
|
Java C# Python
线程等待(Thread Sleep)
线程等待(Thread Sleep)
|
3月前
|
SQL 机器学习/深度学习 算法
【python】python指南(一):线程Thread
【python】python指南(一):线程Thread
44 0