我需要一些帮助来解决如何最好地设计一组用于管理我正在开发的多线程C#应用程序的类。 我已经成功创建了一个ThreadContainer类来启动一系列Threads。 线程从特定网站下载财务数据。 对于传递的每个网站,他们将打开相同的基本网址,但使用不同的符号。 因此,#results等于每个网站传递的符号数。 然后根据请求的网站,线程结果必须以不同的方式处理... 这是简化的ThreadContainer代码:
public class ThreadContainer
{
private int ConcurrentThreads;
private string Website;
public string URL;
private Queue SymQueue;
//Constructor
public ThreadContainer(string website, Queue symQueue)
{
Website = website;
SymQueue = symQueue;
}
//Start
public void start(int concurrent)
{
ConcurrentThreads = concurrent;
//Start the Concurrent Threads
for (int ThreadNum = 1; ThreadNum <= ConcurrentThreads; ThreadNum++)
{
//Get a symbol from the queue
Sym = SymQueue.Dequeue().ToString();
//Build the URL
URL = string.Format(Constants.URL(Website), Sym);
//Create a new Asynch thread
AsynchThread j = new AsynchThread(ThreadNum);
//Start the AsyncThread class with the BackgoundWorker
j.Start(Sym, URL);
}
}
//Method called when the Backgroundworker thread has terminated
private void AsynchThread1_JobCompleted(object sender, EventArgs e)
{
// Get the AsynchThread object
AsynchThread j = (AsynchThread)sender;
//Get the symbol name
String Sym = j.Sym;
//Get the result with the Webpage content
RunWorkerCompletedEventArgs re = e as RunWorkerCompletedEventArgs;
String result = re.Result.ToString();
/* HERE EACH THREAD RETURNS THE WEBSITE CONTENT.
* DEPENDING ON THE WEBSITE THAT WAS REQUESTED SEVERAL ACTIONS MAY BE EXECUTED.
*/
switch(website)
{
case "WebsiteA":
// With WebsiteA I would like to :
// 1. extract the symbol data
// 2. return the result for this symbol to the calling class.
case "WebsiteB":
// With WebsiteB I would like to:
// 1. extract the symbol data (Webpage design is different from WebsiteA)
// 2. store each symbol data in a database table
case "WebsiteC":
// With WebsiteB I would like to:
// 1. extract the symbol data (Webpage design is different from WebsiteA and WebsiteB)
// 2. put the results in a queue
case ...
default:
return "";
}
// Reuse the thread that has just completed
//If there are items left in the queue...
if (SymQueue.Count > 0)
//...get the new Sym value...
NewSym = SymQueue.Dequeue().ToString();
//If NewSym is valid ...
if (!String.IsNullOrEmpty(NewSym))
{
//...build the URL...
String NewURL = string.Format(Constants.URL(Website), NewSym);
//...then restart the thread with the new parameters
j.Start(NewSym, NewURL);
}
}
}
我想知道设计 AsynchThread1_JobCompleted 的最佳方式是什么 方法。 当每个线程终止并且应该执行以下任务时调用此方法:
如何设计方法,以便每次需要添加新网站时都不必修改代码? 另外,我应该使用下载页面来提取数据的相同AsynchThread,保存到数据库等等......还是更好的另一个线程呢? 我想让ThreadContainer类遵循一种单一责任原则......
由于
答案 0 :(得分:0)
在EventArgs中,将委托/操作传递给处理此站点的特定实现。
或者更好地隔离您放置网址的类网站,以及处理要对数据执行的特定工作的方法。然后在EventArgs中为该线程传递Site,并在回调中调用该Site的特定方法。
这样你就可以删除case
语句,并将代码移到它所属的位置。
答案 1 :(得分:0)
如何设计方法,以便我不必修改代码 每次我需要添加一个新网站?
您的设计存在问题,使其变得困难。根据网站,您希望在收到数据后执行不同的操作,因此您的后期处理取决于在网站上。最好的情况是使用一种模式,使其更灵活,因此您的代码维护率很低。但是你没有说明你想在什么情况下这样做,正如你所说:
将数据发送回调用者或将数据保存到数据库或将数据插入队列。
执行此操作所需的逻辑是什么?这基于什么?很难从你的代码中推断出有多重责任的代码。您需要抽象出代码的这些部分。
你可能拥有的是:
使用工厂模式创建一个对象来处理您的请求。您的案例中的请求只是一个带有url和要处理的符号队列的对象,可能是这样的:
interface ISymbolRequest
{
string Url;
Queue<string> Symbols;
}
和处理器可能是这样的:
abstract class SymbolProcessor
{
protected ISymbolRequest _Request;
public SymbolProcessor(ISymbolRequest request)
{
_Request = request;
}
public abstract void GetSymbolData();
public abstract void PostProcessSymbolData();
}
使用工厂模式创建一个处理该请求结果的对象,在您的情况下会将其放回队列,数据库等。
我还建议使用任务并行库(TPL),因为您使用的是.NET 4.0。这使您的代码更易于阅读并且包含许多复杂性。你也可以做很多好事,比如取消,继续等。你可以开始here。