我创建了并行进程,DataTable dtUser
有两行,它应该创建两个浏览器:
Parallel.ForEach(dtUser.AsEnumerable(), items =>
OpenBrowser(items["user"].ToString(), items["pass"].ToString()));
Lapsoft_OneDriver browser;
public void OpenBrowser(string username, string password)
{
browser = new Lapsoft_OneDriver(Browsers.Chrome);
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
}
它创建了两个Chrome进程,但只有第一个进程运行行代码块:
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
第二个过程只初始化新浏览器而不做任何事情。
如果我更改此行:
browser = new Lapsoft_OneDriver(Browsers.Chrome);
到
var browser = new Lapsoft_OneDriver(Browsers.Chrome);
它在工作。
但是另一种方法继续使用变量browser
来执行其他代码。
所以,我必须在一个函数中声明全局变量Lapsoft_OneDriver browser
,以便在另一个方法中使用它。
我的问题是:
为什么使用Lapsoft_OneDriver browser;
创建两个Chrome进程,但只有第一个进程处于活动状态,它会向browser.FindElementById("txtUserName")
插入两个变量值username
而第二个进程没有执行任何操作?
更新:
何时更改代码,我有任何问题。
我将添加更多frmMain_Load代码:
private void frmMain_Load(object sender, EventArgs e)
{
thread = new LThread();
thread.StartedEvent += new LThread.startDelegate(AllCaseProgram);
numLog = int.Parse(dtSetting.Rows[0]["num_Log"].ToString());
}
int numProcess;
private void AllCaseProgram(object args)
{
try
{
switch (numProcess)
{
case 0:
Parallel.ForEach(dtUser.AsEnumerable(), items => Start(items["user"].ToString(), items["pass"].ToString()));
break;
case 1:
ClickCart();
break;
case 2:
Result();
break;
}
}
catch (Exception ex)
{
if (browser != null)
browser.Cleanup();
numProcess = 0;
AllCaseProgram(null);
}
}
按钮StartProgram()_Click
的事件。我启动Thread就像:thread.Start();
你说:应该把这个功能添加到我的程序中。
public static void Start(string user, string pwd)
{
var test = new frmMain();
test.OpenBrowser(user, pwd);
test.ClickCart();
}
我的更新问题是:
似乎函数Start(string user, string pwd)
应更改为函数AllCaseProgram
包括所有切换案例。
frmMain_Load中的变量numLog
的值为3.在函数test.ClickCart()
中我也使用此变量,但值自动更改为0.
代码有任何问题吗?感谢。
LThread
类是:
public class LThread : BackgroundWorker
{
#region Members
public delegate void startDelegate(string ID);
public event startDelegate StartedEvent;
private static int RandNumber(int Low, int High)
{
Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
int rnd = rndNum.Next(Low, High);
return rnd;
}
protected override void OnDoWork(DoWorkEventArgs e)
{
StartedEvent(RandNumber(100,10000).ToString()); //put whatever parameter suits you or nothing
base.OnDoWork(e);
e.Result = e.Argument;
}
BackgroundWorker bwThread;
// Main thread sets this event to stop worker thread:
public Boolean bwIsRun;
int m_time_delay = 10000;
Delegate m_form_method_run;
Delegate m_form_method_stop;
Form m_type_form;
#endregion
#region Functions
public void Start()
{
try
{
bwIsRun = true;
this.RunWorkerAsync();
}
catch { }
}
public void Stop()
{
try
{
bwIsRun = false;
}
catch { }
}
private void StartToListen(object sender, DoWorkEventArgs e)
{
while (true)
{
Thread.Sleep(m_time_delay);
if (bwIsRun == true)
{
m_type_form.Invoke(m_form_method_run);
}
else
{
BackgroundWorker bwAsync = sender as BackgroundWorker;
if (bwAsync.CancellationPending)
{
e.Cancel = true;
return;
}
break;
}
}
}
#endregion
}
答案 0 :(得分:3)
您应该为每次测试运行封装您的状态。这样你就有了一个负责启动浏览器,执行一个或多个动作的类,同时保持属于单个运行的所有必需状态仅为一个实例私有,而你可以拥有许多实例(如果资源允许)。
// this is NOT a winform, this is a new and seperate class ...
// don't try to mix this with an WinForm, that will fail
public class BrowserTestRunner
{
// only this Test instances uses this browser
Lapsoft_OneDriver browser;
private void OpenBrowser(string username, string password)
{
browser = new Lapsoft_OneDriver(Browsers.Chrome);
browser.GoToUrl(link);
browser.FindElementById("txtUserName").SendKeys(username);
browser.FindElementById("txtpassword").SendKeys(password);
// you probably want to click on something here
}
// some other test
private void ClickCart()
{
browser.FindElementById("btnCart").Click();
}
// add other actions here
// this starts the test for ONE browser
public static void Start(string user, string pwd)
{
var runner = new BrowserTestRunner();
runner.OpenBrowser(user, pwd);
// wait for stuff, check data, prepare the next steps
// for example
// runner.ClickCart();
// other actons here
}
}
现在您可以根据需要创建任意数量的Test类实例,而类的每个实例都可以管理自己的内部状态,而不会干扰其他实例:
Parallel.ForEach(dtUser.AsEnumerable(), items =>
BrowserTestRunner.Start(items["user"].ToString(), items["pass"].ToString()));
如果你想从你的背景工作者那里开始:
private void AllCaseProgram(object args)
{
try
{
switch (numProcess)
{
case 0:
Parallel.ForEach(
dtUser.AsEnumerable(),
items => BrowserTestRunner.Start(items["user"].ToString(), items["pass"].ToString()));
break;
case 1:
ClickCart();
break;
case 2:
Result();
break;
}
}
catch (Exception ex)
{
if (browser != null)
browser.Cleanup();
numProcess = 0;
AllCaseProgram(null);
}
}
无论如何:不要再次启动主表单。只需将WinForm与用于操作浏览器的代码分开即可。这意味着您必须将与浏览器交互的代码移动到BrowserTestRunner。不要试图在WinForm类中保留你的selenium东西的逻辑,因为那注定要失败。正如您已经体验过的那样。
答案 1 :(得分:1)
你在这里得到的是一种竞争条件。在处理类中的单个字段时,有两个线程无法相处。您的问题仅您没有足够的空间来存储所需的所有浏览器实例。
基本上,第一个线程进入方法,创建chrome浏览器的实例并将其存储在变量中。然后第二个线程进入函数并执行相同的操作。但它也将实例存储在同一个变量中。现在第一个线程继续并转到链接。但它正在使用的实例已经被第二个线程所取代。等等。这可能发生在相反的线程中,或者在处理更多行之后可能发生重叠。但它肯定会出错。
解决问题的方法是,您注意到通过添加var
使变量成为本地变量。这样两个线程都使用不同的变量。
现在你说你需要另一个函数中的变量。问题是:你需要两者吗?你只需要一个吗?你需要一个具体的吗?
如果您只需要一个,只需在函数中添加如下行,即可将变量存储在全局变量中:
this.browser = browser;
所以它总体上看起来像这样:
Lapsoft_OneDriver browser;
public void OpenBrowser(string username, string password)
{
var localBrowser = new Lapsoft_OneDriver(Browsers.Chrome);
localBrowser.GoToUrl(link);
localBrowser.FindElementById("txtUserName").SendKeys(username);
localBrowser.FindElementById("txtpassword").SendKeys(password);
this.browser = localBrowser;
}
我更改了本地浏览器变量的名称,因此可以更清楚地使用哪个变量。请注意,任何一个创建的浏览器都可以在变量中结束。
如果您需要特定的一个,您必须确定是否有正确的一个并在此之后存储结果。
如果您需要两者,则必须将它们存储在列表中。命名空间System.Collections.Concurrent
提供了可以由多个线程同时处理的列表。