好的,伙计们,因为我是多线程新手,所以我会在这里为这个问题写很多代码,我不想遗漏任何东西。
问题: 我有一个用户控件,我从中下载来自供应商的数据提要文件。我希望能够一次处理这几个,但是,按照我设置 BackGroundWorker 的方式,它一次只运行两个下载,而其他的似乎“排队”等待另外两个中的一个在开始之前完成。那些正在等待的,使其进入 FTPDownload() 中的第一个“GetResponse”调用。
如果我取消任何一个当前正在运行的下载,“等待中”的下载永远不会开始。
我在 DoWork 事件中使用 States 让它决定启动哪个流程,并且在 WorkComplete 事件中读取相同的状态来决定如何结束流程。
一切正常,除了我描述的。
编码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GooeyPC_CCSE;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
namespace Gooey_Manager
{
public partial class DFProgressControl : UserControl
{
BackgroundWorker _bg_worker = new BackgroundWorker();
#region Private Members and Public Accessors
/// <summary>
/// Get/Set DFProgressControl Vendor Name
/// </summary>
public string VendorName
{
get { return lblDFP_VendorName.Text; }
set { lblDFP_VendorName.Text = value; }
}
/// <summary>
/// Get/Set DFProgressControl CurrentState
/// </summary>
public string CurrentState
{
get { return lblDFP_CurrentState.Text; }
set { lblDFP_CurrentState.Text = value; }
}
/// <summary>
/// Get/Set DFProgressControl RecordOfNum
/// </summary>
public string RecordOfNum
{
get { return lblDFP_RecordOfNum.Text; }
set { lblDFP_RecordOfNum.Text = value; }
}
private AffiliateServiceProviderItem _aff_svc_prov = new AffiliateServiceProviderItem();
public AffiliateServiceProviderItem AffiliateSvcProvider
{
get { return _aff_svc_prov; }
set { _aff_svc_prov = value; }
}
private VendorSiteInfoItem _vsi = new VendorSiteInfoItem();
public VendorSiteInfoItem VendorSiteInfo
{
get { return _vsi; }
set { _vsi = value; }
}
/// <summary>
/// DataFeeder States
/// </summary>
public enum ObjectState
{
Nothing = 0,
Canceling = 1,
Download = 2,
Unzip = 3,
Prestage = 4,
Validate = 5,
Move_to_DB = 6,
Destroy = 7
}
private ObjectState _feeder_state = ObjectState.Nothing;
public ObjectState FeederState
{
get { return _feeder_state; }
set { _feeder_state = value; }
}
#endregion
/// <summary>
/// Constructor
/// </summary>
/// <param name="vendor_id"></param>
public DFProgressControl(int vendor_id)
{
InitializeComponent();
//Initialize member data
this._vsi = GooeyDataFactory.GetVendorSiteInfo(vendor_id);
this._aff_svc_prov = GooeyDataFactory.GetAffiliatSvcProviderByID(_vsi.AffiliateSvcProviderID);
VendorName = _vsi.SiteName;
CurrentState = FeederState.ToString();
RecordOfNum = "";
//Background worker settings
_bg_worker.WorkerSupportsCancellation = true;
//Background worker event handlers
_bg_worker.DoWork += new DoWorkEventHandler(workerDataFeeder_DoWork);
_bg_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerDataFeeder_RunWorkerCompleted);
}
private void workerDataFeeder_DoWork(object sender, DoWorkEventArgs e)
{
switch (FeederState)
{
case ObjectState.Download:
this.ProductDataFeedDownload(VendorName);
break;
case ObjectState.Unzip:
break;
default:
break;
}
}
private void workerDataFeeder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
switch (FeederState)
{
case ObjectState.Download:
lblDFP_RecordOfNum.Text = "Download Complete";
FeederState = ObjectState.Nothing;
break;
case ObjectState.Unzip:
FeederState = ObjectState.Nothing;
break;
case ObjectState.Canceling:
lblDFP_RecordOfNum.Text = "Operation Canceled";
pbDFP_ProgressBar.Value = 0;
FeederState = ObjectState.Nothing;
break;
default:
break;
}
CurrentState = FeederState.ToString();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//Data Feeder Process menu items
/////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Download vendor's data feed file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_bg_worker.IsBusy)
{
MessageBox.Show(VendorName + " Data Feeder is busy with " + CurrentState + "\nCancel Operation to do something else.");
}
else
{
FeederState = ObjectState.Download;
CurrentState = FeederState.ToString();
lblDFP_RecordOfNum.Text = "Preparing to download...";
pbDFP_ProgressBar.Value = 0;
_bg_worker.RunWorkerAsync();
}
}
/// <summary>
/// Cancel current operation
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cancelOperationToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_bg_worker.IsBusy)
{
FeederState = ObjectState.Canceling;
}
else
{
MessageBox.Show("No operation to stop for " + VendorName + " data feeder.");
}
}
private void destroyToolStripMenuItem_Click(object sender, EventArgs e)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//Data Feed Stuff
/////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Download Vendor's data feed
/// </summary>
/// <param name="vendor_name"></param>
/// <param name="pb"></param>
/// <param name="status_text"></param>
/// <returns></returns>
public byte[] ProductDataFeedDownload(string vendor_name)
{
string filename = Regex.Match(this._vsi.DFFTPProd, @"(\w|[-.])+$", RegexOptions.IgnoreCase).ToString();
byte[] data_feed = FTPDownload(this._vsi.DFFTPProd, this._aff_svc_prov.FTPUser, this._aff_svc_prov.FTPPassword);
if (data_feed.Length != 0)
{
//Write the bytes to a file
//NOTE: Do I really need to be Using Windows.Forms for this. System.IO should have the necessary methods?
SaveFileDialog save_data_feed = new SaveFileDialog();
save_data_feed.FileName = this._vsi.DFFileTextProd + "\\" + filename;
FileStream newFile = new FileStream(save_data_feed.FileName, FileMode.Create);
newFile.Write(data_feed, 0, data_feed.Length);
newFile.Close();
}
return data_feed;
}
/// <summary>
/// Connects to the FTP server and downloads the file
/// </summary>
/// <param name="FTPAddress"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="pb"></param>
/// <param name="status_text"></param>
/// <returns></returns>
private byte[] FTPDownload(string FTPAddress, string username, string password)
{
byte[] downloadedData = new byte[0];
try
{
//Create FTP request
//Note: format is ftp://server.com/file.ext
FtpWebRequest request = FtpWebRequest.Create(FTPAddress) as FtpWebRequest;
//Get the file size first (for progress bar)
request.Method = WebRequestMethods.Ftp.GetFileSize;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true; //don't close the connection
int dataLength = 999999;
dataLength = (int)request.GetResponse().ContentLength;//This does not work for Tigerdirect download. Causes error.
//Now get the actual data
request = FtpWebRequest.Create(FTPAddress) as FtpWebRequest;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false; //close the connection when done
//Streams
FtpWebResponse response = request.GetResponse() as FtpWebResponse;
Stream reader = response.GetResponseStream();
//Download to memory
MemoryStream memStream = new MemoryStream();
byte[] buffer = new byte[1024]; //downloads in chuncks
decimal bytesDownloaded = 0;
bool cancel = false;
while (!cancel)
{
//Try to read the data
int bytesRead = reader.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
else
{
//Write the downloaded data
memStream.Write(buffer, 0, bytesRead);
bytesDownloaded += bytesRead;
//Show Progress
ShowProgress(bytesRead, dataLength);
//Check for cancelation request
if (this.FeederState == ObjectState.Canceling)
{
DialogResult result = MessageBox.Show("Click OK to stop current operation for " + VendorName + " data feeder.", "Cancel?", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
_bg_worker.CancelAsync();
cancel = true;
memStream = null;
}
else
{
this.FeederState = ObjectState.Download;
}
}
}
}
//Convert the downloaded stream to a byte array
if (memStream != null)
{
downloadedData = memStream.ToArray();
memStream.Close();
}
//Clean up
reader.Close();
response.Close();
}
catch (Exception e)
{
}
username = string.Empty;
password = string.Empty;
return downloadedData;
}
/// <summary>
/// Update progress bar and Record of Num values
/// </summary>
/// <param name="progress"></param>
/// <param name="upper_bound"></param>
private void ShowProgress(int progress, int upper_bound)
{
//Current State
lblDFP_CurrentState.Invoke((MethodInvoker)delegate
{
lblDFP_CurrentState.Text = this.FeederState.ToString();
});
//Current Progress
pbDFP_ProgressBar.Invoke((MethodInvoker)delegate
{
pbDFP_ProgressBar.Maximum = upper_bound;
if (pbDFP_ProgressBar.Value + progress < upper_bound)
{
pbDFP_ProgressBar.Value += progress;
}
else
{
pbDFP_ProgressBar.Value = upper_bound;
}
});
//Current value of progress over upper bound
lblDFP_RecordOfNum.Invoke((MethodInvoker)delegate
{
lblDFP_RecordOfNum.Text = (Convert.ToDecimal(pbDFP_ProgressBar.Value)/1024/1024).ToString("0.00") + " of " + (Convert.ToDecimal(upper_bound)/1024/1024).ToString("0.00") + " MB read.";
});
}
}
}
我确定我在这里遗漏了一些基本的东西,我希望有人能启发我。就像我说的,线程的东西很新——虽然我很高兴我能做到这一点;它没有按我的意愿工作。
提前致谢。
编辑:我还应该添加我如何实例化 UserControl
//Data Feed Tab
private void btnStartDataFeed_Click(object sender, EventArgs e)
{
DFProgressControl _df_prog_ctl = new DFProgressControl((int)lbDataFeedVendorList.SelectedValue);
if (_df_control_list.Find(o => o.VendorSiteInfo.VendorSiteInfoID == (int)lbDataFeedVendorList.SelectedValue) == null)
{
//Add instance of object to Data Feeder list
flowLayoutPanel1.Controls.Add(_df_prog_ctl);
_df_control_list.Add(_df_prog_ctl);
}
else
{
MessageBox.Show("There is already an active data feeder for " + GooeyDataFactory.GetVendorSiteInfo(_df_prog_ctl.VendorSiteInfo.VendorSiteInfoID).SiteName + " doing " + _df_prog_ctl.FeederState + ".");
}
}