delegate的使用-跨线程修改页面

delegate的使用-跨线程修改页面

前情提要

没事写了个爬取网页小说的方法,使用正则表达式提取页面的数据,然后获取写到文件中,当然不会破解会员之类的,还没到达那么高深的地步,所以,如果你是大佬来的,请指教。

正则表达式

既然谈到了正则,就顺便写一下,也回顾一下原来是怎么写的。

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

菜鸟教程大部分都有,正则表达式的使用难点就是量比较大,记不住啊!
示例:

//获取图片的跳转路径
string strreg_img = "(?<=(<a[\\s\\S]*href=\"))[\\s\\S]*?(?=(\"[\\s\\S]*data-bid))";

这个是获取图片的路径的,网页中的图片基本上都是有跳转功能的,在小说网上,小说的封面终究包含了跳转到该小说的路径,也就是一个A标签了,路径就是href(当然,肯定是还有其他的,不过现在先不谈,主要也不会)。

a标签的格式:<a href="url" ></a>

因为是需要路径,也就是我们只需要使用正则表达式匹配出url就可以了。因为网站的a标签中有一个data-bid的属性,所以就是匹配<a href="" data-bid之间的数据就可以了。但是需要注意的是,每个属性之间都是有空格的,而且空格的数量不一定都是一。
那么什么读没有的匹配应该就是(<a href=")(" data-bid),中间的字符需要处理掉。先看一下菜鸟里面的说明
来自菜鸟教程
好了,反正大概就是匹配所有文字的地方就是\s\S就没跑了,但是也说了,多余的数量可能是不止一个的,那么就需要匹配0次一次乃至多次了。
再看。
来自菜鸟教程
*可以匹配多次,那就是[\\s\\S]*注意转义,然后加一个?构成非贪婪模式

非贪婪模式
非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。
也就是对于acbacb来说,a[\s\S]*b 贪婪模式就是匹配出abab,a[\s\S]*?b非贪婪模式就是匹配出ab

也就变成了这样[\\s\\S]*?,那么没有?就是贪婪模式了。
OK,开始替换。

就变成了这样的:(<a[\\s\\S]*href=")[\\s\\S]*?("[\\s\\S]*data-bid)

但是,开始的正则表达式还包括了?<=?=,这个就是正向肯定预查和反向肯定预查了。
来自菜鸟教程
加上就完事了。
最终就变成了(?<=(<a[\\s\\S]*href=\"))[\\s\\S]*?(?=(\"[\\s\\S]*data-bid))

delegate 委托

因为爬取小说的原因,了解到了多线程可以提高速度(盲目了,但是就当是练手了),所以就使用了多线程同时爬取。但是,由于多个线程同时进行的原因,但看爬出的数据没办法知道,哪一个开始了,哪一个结束了。所以,首先定义了几个变量

		//开始的标题,设置为volatile可以让所有线程可见,保证线程之间不冲突
        public volatile static string subject = "";
        //设置一个私有的标题,在线程开始修改公共subject的时候,记录上一次的标题
        private static string subject_ = "";
        //记录结束的标题,设置为volatile可以让所有线程可见,保证线程之间不冲突
        public volatile static string subject_end = "";
        //设置一个私有的标题,在线程开始修改公共subject的时候,记录上一次的标题
        private static string subject_end_ = "";

使用volatile让变量对线程透明,也就是一个线程改变了subject的值,其余的线程看到的就是改变的值(通俗理解)。下面的私有变量都是进行对比使用的。

		public void changeText(object o)
        {
            while (true)
            {
                //判断,如果subject被线程修改,那么就使用代理写出来
                if (subject != subject_)
                {
                    subject_ = subject;
                    richTextBox1.Text += "开始爬取【" + subject_ + "】\r\n";
                }

                if (subject_end != subject_end_)
                {
                    subject_end_ = subject_end;
                    richTextBox1.Text += "【" + subject_end_ + "】爬取完成" + "\r\n";
                }
            }
        }

调用

private void button1_Click(object sender, EventArgs e)
        {
            int maxThreadNum, portThreadNum;
            int minThreadNum;
            ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum);
            ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum);
            //加入线程池
            ThreadPool.QueueUserWorkItem(new WaitCallback(changeText), 1);
            ThreadPool.QueueUserWorkItem(new WaitCallback(getbook), 1);
        }

看起来没啥问题,vs也没有报错,但是运行的话
来自VS截图
主线程创建的控件,没办法从其他线程进行修改。,也就是跨线程调用windows窗体控件的问题。
百度ing… …
问题的解决方法也简单-delegate委托。
委托是什么?
用我的理解来说就是两个除了方法体不同,其他的参数或者返回值全部相同的就可以使用delegate委托一次运行两个方法。
委托的定义

		/// <summary>
        /// 代理,使用代理的方法才能跨线程访问页面,为页面的控件赋值
        /// </summary>
        private delegate void newDelegate();
        public void test(){
			newDelegate nd = new newDelegate();
			nd();
		}
		

怎么为向委托委托一个方法?

		public void console1(){
			Console.WriteLine("输出1");
		}
		public void console2(){
			Console.WriteLine("输出2");
		}
		public void run(){
			newDelegate nd = new newDelegate(console1);
			nd += console2;//使用+=可以增加委托中委托的方法
			nd();
		}

委托原来就是这么个玩意儿,简单,换一下就行了。

public void changeText(object o)
        {
            while (true)
            {
                //判断,如果subject被线程修改,那么就使用代理写出来
                if (subject != subject_)
                {
                    subject_ = subject;
                    
                    newDelegate nd = new newDelegate(this.refresh);
                    
                    nd();
                }

                if (subject_end != subject_end_)
                {
                    subject_end_ = subject_end;

                    newDelegate nd = new newDelegate(this.refresh2);

                    nd();
                }
            }
        }

这样的话,运行。
图片来自VS截图
搞我是不?
查了一下,如果是跨线程的话,应该是需要的线程调用委托,而不是当前线程调用,那样的话不就是?

		//
        // 摘要:
        //     在拥有此控件的基础窗口句柄的线程上执行指定的委托。
        //
        // 参数:
        //   method:
        //     包含要在控件的线程上下文中调用的方法的委托。
        //
        // 返回结果:
        //     正在被调用的委托的返回值,或者如果委托没有返回值,则为 null。
        [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        public object Invoke(Delegate method);

Invoke方法调用委托,也就变成了

public void changeText(object o)
        {
            while (true)
            {
                //判断,如果subject被线程修改,那么就使用代理写出来
                if (subject != subject_)
                {
                    subject_ = subject;
                    
                    newDelegate nd = new newDelegate(this.refresh);
                    //必须使用Invoke调用修改页面的方法,直接调用还是属于线程调用,会报错
                    this.Invoke(nd);
                }

                if (subject_end != subject_end_)
                {
                    subject_end_ = subject_end;

                    newDelegate nd = new newDelegate(this.refresh2);

                    this.Invoke(nd);
                }
            }
        }

F5!ok!
在这里插入图片描述
问题解决!
以上就是全部了。如有指教,评论区见。
撤了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值