Thread的一个简单问题,有答案马上结帖
我在做一个数据采集的程序,当同时在多个网站采集数据时,如果有采集网站出现故障打不开或打开得非常慢时,就会导致异常使程序无法响应。
我的想法是当部分网站采集不了时,并不影响程序的运行,其他的采集任务还是能继续。
觉得只有用多线程处理,但之前没有应用过,所以请大家给个示例代码,谢谢!~
主要代码如下:
//采集
private void getInfo(string url,Encoding coding)
{
//...
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 5000;
try
{
WebResponse response = request.GetResponse();
Stream strm = response.GetResponseStream();
StreamReader sr = new StreamReader(strm, coding);
collectInfo = sr.ReadToEnd();
strm.Close();
sr.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
//...
}
//主程序里调用
private void button1_Click(object sender, EventArgs e)
{
//...
CollectionInfo coll = new CollectionInfo();
coll.getInfo( "http://www.site1.com/index.html ",Encoding.UTF8);
coll.getInfo( "http://www.site2.com/index.aspx ",Encoding.UTF8);
coll.getInfo( "http://www.site3.com/index.php ",Encoding.UTF8);
//...
}
当我点采集的时候,遇到连接不上的网页,就会出错。我设置了HttpWebRequest的timeout也没用。
请大家看看如何利用多线程改一下
[解决办法]
private void run1()
{
CollectionInfo coll = new CollectionInfo();
coll.getInfo( "http://www.site1.com/index.html ",Encoding.UTF8);
}
private void run2()
{
CollectionInfo coll = new CollectionInfo();
coll.getInfo( "http://www.site2.com/index.aspx ",Encoding.UTF8);
}
private void run3()
{
CollectionInfo coll = new CollectionInfo();
coll.getInfo( "http://www.site3.com/index.php ",Encoding.UTF8);
}
private void button1_Click(object sender, EventArgs e)
{
ThreadStart ts1=new ThreadStart(run1);
Thread tr1=new Thread(ts1);
tr1.Start();
ThreadStart ts2=new ThreadStart(run2);
Thread tr2=new Thread(ts2);
tr2.Start();
ThreadStart ts3=new ThreadStart(run3);
Thread tr3=new Thread(ts3);
tr3.Start();
}
[解决办法]
提交的是个方法的名称如run3()
不能够传递参数的.
问题相关: 主线程与创建线程的交换数据
[解决办法]
说一个思路吧...
这种情况可以建一个任务队列,将你所要采集的所有URL存储在这个队列中(可以用ArrayList,或者List <string> 等等),多个线程从队列中取出任务进行抓取,当然取出一条任务后,就要将任务从队列中删除,即维护好任务队列...
在抓取过程中,如果遇到异常,捕获一下,可以将异常写到日志文件中,而不是抛出,这样就算出现异常也不会影响整个程序的运行...
有关多线程可以看看MSDN..
[解决办法]
看一这个就明白了,用类的方法作线程执行的过程.
这样就可以用类属性传值地。
分静态方法和非静态方法两种使用办法。
using System;
using System.Threading;
class Test
{
static void Main()
{
// To start a thread using a static thread procedure, use the
// class name and method name when you create the ThreadStart
// delegate. Beginning in version 2.0 of the .NET Framework,
// it is not necessary to create a delegate explicityly.
// Specify the name of the method in the Thread constructor,
// and the compiler selects the correct delegate. For example:
//
// Thread newThread = new Thread(Work.DoWork);
//
ThreadStart threadDelegate = new ThreadStart(Work.DoWork);
Thread newThread = new Thread(threadDelegate);
newThread.Start();
// To start a thread using an instance method for the thread
// procedure, use the instance variable and method name when
// you create the ThreadStart delegate. Beginning in version
// 2.0 of the .NET Framework, the explicit delegate is not
// required.
//
Work w = new Work();
w.Data = 42;
threadDelegate = new ThreadStart(w.DoMoreWork);
newThread = new Thread(threadDelegate);
newThread.Start();
}
}
class Work
{
public static void DoWork()
{
Console.WriteLine( "Static thread procedure. ");
}
public int Data;
public void DoMoreWork()
{
Console.WriteLine( "Instance thread procedure. Data={0} ", Data);
}
}
不过还是像楼上说的,把Url都存在一个队列里,把这个队列当作类的属性。
然后声明几个类的实例,调用即可。
不过要注意这几个线程的同步,用同步对象防止对队列的重入操作。
[解决办法]
private void button1_Click(object sender, EventArgs e)
{
this.Start( "http://www.site1.com/index.html ");
this.Start( "http://www.site2.com/index.aspx ");
this.Start( "http://www.site3.com/index.php ");
}
private void Start(string url)
{
myThread info=new myThread(url);
ThreadStart ts=new ThreadStart(info.Run);
Thread thread=new Thread(ts);
thread.Start();
}
public class myThread
{
private string url=string.Empty;
public myThread(string url){this.url=url;}
string Url{get{return url;}set{url=value;}}
public void Run()
{
new CollectionInfo().getInfo(url,Encoding.UTF8);
}
}
做程序要学会思考,有时可以变通。
你还可以用委托的方式更改。
[解决办法]
1.定义一个委托
public delegate void DGetInfo(string url, Encoding coding);
2.用委托的异步调用 BeginInvoke 比线程方便,而且传递参赛
用的时候
DGetInfo dinfo = new DGetInfo(getInfo);
dinfo.BeginInvoke( "http://www.site1.com/index.html ", Encoding.UTF8, null, null);
具体如何封装,就是你自己的问题了
不过这种东西没有用 MessageBox 提示错误的
如果多线程,会弹出很多的 MessageBox,写到文件或打印到命令行就可以
还有这种东西,一般是有一个队列的,
把初始参数先放到队列,然后线程去取,下载一个网页后,分析连接
在放回队列,然后再重队列里去一个 url 继续处理
如果重试验1。。n 次没有成功就放弃本次的请求
在重队列取一个新的去试验