下面的C#程序是一个股票应用程序,可以模拟股票的涨跌。我知道当索引超出for循环范围时会引发ArgumentOutOfException。但是,在多次重新运行该程序之后,我注意到该异常只是偶尔抛出。运行该程序10次后,在该行上将异常抛出1-4次
stocks.remove(stocks[i]).
看我的程序,它的线程同步部分是否引起了问题?如果不是,那么是什么原因导致偶尔抛出该异常,而不是根本不引发或每次运行都引发该异常?
Stock.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3
{
//delegate to display stock information
public delegate void StockNotification(String stockName, double currentValue, double initialV, int stockquantity);
public class Stock
{
public System.Object lockThis = new System.Object(); //lock each stock object for raising and invoking event
private String name;
private double initValue; //starting or initial value
private int stkAmt;
private double current; //curent stock value
private int numChanges; //number of times stock value changes
private int maxChange; //the range within a stock can change every time unit
private double notifyThreshold; //the threshold at which collection of customers for a stock are notified
static string[] operations = { "+", "-" }; //array of operations to change stock value (+ or -)
Thread thr; //thread object
/*default constructor*/
public Stock() { }
/*constructor w/ given parameter values*/
public Stock(String stkName, int starting, int amt, int max, int thresh)
{
name = stkName;
initValue = starting;
current = initValue;
stkAmt = amt;
maxChange = max;
notifyThreshold = thresh;
thr = new Thread(new ThreadStart(Activate));
thr.Start(); //start thread
}
/*property that controls the variable stockAmt*/
public int StockQuantity
{
get { return stkAmt; }
set { stkAmt = value; }
}
/*read-only property that gets name of stock*/
public string StockName
{
get { return name; }
}
/*changes stock value after a period of time
end method when # of changes to stock value match
stock quantity*/
public void Activate()
{
numChanges = 0;
for (; ; )
{
Thread.Sleep(500);
ChangeStockValue();
if (numChanges == stkAmt)
{
return;
}
}
}
/*change stock value - add number between 1 and some specified number
raise event if difference between current and starting values exceed threshold*/
public void ChangeStockValue()
{
Random rnd = new Random();
int oper = rnd.Next(operations.Length);
if(oper == 0) //add random number to current stock value
{
current += rnd.Next(10, maxChange);
}
else if(oper == 1) //subtract random number from current stock value
{
current -= rnd.Next(10, maxChange);
}
numChanges++;
if (numChanges == stkAmt) //# of times stock values changes matches stock's quantity
{
return;
}
else
{
double gainRounded = Math.Round(((current - initValue) / initValue) * 100,2); //get stock gain/loss
//stock gain/loss is outside stock's threshold range (ex: -5% to 20% w/ 20 being threshold)
if (gainRounded > this.notifyThreshold || gainRounded < this.notifyThreshold * -1)
{
lock(lockThis) //raise stock event 1 stock obj at a time
{
RaiseEvent(); //send data to StockEventData object; send object attributes to listener
}
}
}
}
/*this method sends stock information to the event listener
event type StockNotification is invoked*/
protected virtual void RaiseEvent()
{
StockEventData stock = new StockEventData();
stock.stkName = name;
stock.currentVal = current;
stock.initialVal = initValue;
stock.quantityStk = stkAmt;
StockEvent?.Invoke(stock.stkName, stock.currentVal, stock.initialVal, stock.quantityStk);
}
public event StockNotification StockEvent; //event of type StockNotification
}
/*this class brings stock data to the event listener*/
public class StockEventData : EventArgs
{
public String stkName { get; set; }
public double currentVal { get; set; }
public double initialVal { get; set; }
public int quantityStk { get; set; }
}
}
StockCustomer.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3
{
public class StockCustomer
{
private String customer;
private List<Stock> stocks;
private readonly System.Object lock1 = new System.Object(); //lock object for displaying stock info in file
private double gainThreshold; //customer's personal threshold
/*default constructor*/
public StockCustomer(String name, double gainLimit)
{
customer = name;
gainThreshold = gainLimit;
stocks = new List<Stock>();
}
/*add stock to a list
@param s - stock object*/
public void AddStock(Stock s)
{
stocks.Add(s);
s.StockEvent += Notify; //add stock to the event handler
}
/*event handler that displays stock information in console and file
@param stkName - stock name
@param currentVal - current stock value
@param initVal - initial stock value
@param quantityStk - stock quantity*/
private void Notify(string stkName, double currentVal, double initVal, int stockQuantity)
{
string net = ""; //string that will contain either gain or loss text
double gainOrLoss = Math.Round(((currentVal - initVal) / initVal) * 100.00, 2); //get stock value
//if stock gain is outisde range of customer's threshold, sell the stock
if (gainOrLoss > gainThreshold || gainOrLoss < gainThreshold*-1)
{
if(gainOrLoss < gainThreshold * -1) //customer's stock value is a loss
{
gainOrLoss *= -1;
net = "% loss";
}
else //customer's stock value is a gain
{
net = "% gain";
}
//remove stock from list after displaying in console and file
for (int i = 0; i < stocks.Count; i++)
{
if (stocks[i].StockName == stkName)
{
//display stock info to console
Console.WriteLine(this.customer.PadRight(12) + stkName.PadRight(12) + initVal.ToString().PadRight(5) +
currentVal.ToString().PadRight(7) + gainOrLoss + net);
// Create a string array with lines of stock info, current date and time
string[] lines = { DateTime.Now.ToString("MM/dd/yyyy").PadRight(12),
DateTime.Now.ToString("hh:mm:ss").PadRight(13),this.customer.PadRight(12),
stkName.PadRight(12), initVal.ToString().PadRight(5),
currentVal.ToString().PadRight(7) + gainOrLoss + net};
// Set a variable to the My Documents path.
string mydocpath1 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
mydocpath1 = Path.Combine(mydocpath1, "WriteLines.txt");
lock (lock1) //1 stock object displayed in file; repeats for other stock objects
{
//create a stream for a file
using (FileStream fs = new FileStream(mydocpath1, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
// Write the string array to a new file named "WriteLines.txt".
using (StreamWriter outputFile = new StreamWriter(fs))
{
foreach (string line in lines)
{
outputFile.Write(line); //write stock info to file
}
outputFile.WriteLine("");
outputFile.Close();
outputFile.Dispose();
}
fs.Close();
fs.Dispose();
}
}
stocks.Remove(stocks[i]);
}
}
}
}
}
}
Program.cs(主要方法)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3
{
class Program
{
static void Main(string[] args)
{
Stock stock1 = new Stock("Technology", 160, 25, 20, 30);
Stock stock2 = new Stock("Retail", 140, 20, 10, 15);
Stock stock3 = new Stock("Banking", 90, 25, 15, 25);
Stock stock4 = new Stock("Commodity", 500, 30, 20, 22);
StockCustomer b1 = new StockCustomer("Andrew",28);
b1.AddStock(stock1);
b1.AddStock(stock4);
b1.AddStock(stock3);
b1.AddStock(stock2);
StockCustomer b2 = new StockCustomer("Harrison",25);
b2.AddStock(stock3);
b2.AddStock(stock4);
b2.AddStock(stock2);
b2.AddStock(stock1);
StockCustomer b3 = new StockCustomer("Jimmy",30);
b3.AddStock(stock2);
b3.AddStock(stock1);
b3.AddStock(stock3);
b3.AddStock(stock4);
Console.ReadKey();
}
}
}
答案 0 :(得分:0)
出现异常的原因是,您在循环“向上”数组时要删除循环中的项。假设您的数组中有2个项目,而您删除了第一个。在第二遍循环中,您将尝试访问位置1处的项目,但是数组现在在位置0处仅包含一项,因此例外。
在这种情况下,我采取的方法是始终向后遍历数组:
for (int i = stocks.Count - 1; i >= 0; i--)