If I'm simply going to do the following to see what called me,
var st = new StackTrace();
var callingMethod = st.GetFrame(1).GetMethod()
would it be cheaper to just get that specific frame?
var sf = new StackFrame(1);
var callingMethod = sf.GetMethod()
I tested with the code below, but I'm unsure if my methods are sound.
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
var method = new StackFrame(1, false);
}
sw.Stop();
Trace.WriteLine(sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
var method = new StackTrace().GetFrame(1);
}
sw.Stop();
Trace.WriteLine(sw.ElapsedMilliseconds);
// Results
// StackFrame: 850
// StackTrace: 1334
Is my approach (and results) correct?
Edit
I'd use the Caller Information attributes, however, I'm stuck in .NET 3.5 for the time being.
See recommendations for the compilation the correct benchmark. You should use prime number of iterations (for suppress JIT Loop unwinding optimization), run benchmark in Release mode without debugging, use cache warmup, etc.
I added your example in BenchmarkDotNet, look to StackFrameProgram.cs:
public class StackFrameProgram
{
private const int IterationCount = 100001;
public void Run()
{
var competition = new BenchmarkCompetition();
competition.AddTask("StackFrame", () => StackFrame());
competition.AddTask("StackTrace", () => StackTrace());
competition.Run();
}
private StackFrame StackFrame()
{
StackFrame method = null;
for (int i = 0; i < IterationCount; i++)
method = new StackFrame(1, false);
return method;
}
private StackFrame StackTrace()
{
StackFrame method = null;
for (int i = 0; i < IterationCount; i++)
method = new StackTrace().GetFrame(1);
return method;
}
}
There is my result (Intel Core i7-3632QM CPU 2.20GHz):
x86, .NET 3.5:
StackFrame : 1035ms
StackTrace : 1619ms
x64, .NET 3.5:
StackFrame : 981ms
StackTrace : 1754ms
x86, .NET 4.0:
StackFrame : 735ms
StackTrace : 1150ms
x64, .NET 4.0:
StackFrame : 637ms
StackTrace : 880ms
Let's look inside:
public StackFrame.ctor(int skipFrames, bool fNeedFileInfo)
{
this.InitMembers();
this.BuildStackFrame(skipFrames, fNeedFileInfo);
}
private void StackFrame.BuildStackFrame(int skipFrames, bool fNeedFileInfo)
{
StackFrameHelper sfh = new StackFrameHelper(fNeedFileInfo, null);
StackTrace.GetStackFramesInternal(sfh, 0, null);
int numberOfFrames = sfh.GetNumberOfFrames();
skipFrames += StackTrace.CalculateFramesToSkip(sfh, numberOfFrames);
if ((numberOfFrames - skipFrames) > 0)
{
this.method = sfh.GetMethodBase(skipFrames);
this.offset = sfh.GetOffset(skipFrames);
this.ILOffset = sfh.GetILOffset(skipFrames);
if (fNeedFileInfo)
{
this.strFileName = sfh.GetFilename(skipFrames);
this.iLineNumber = sfh.GetLineNumber(skipFrames);
this.iColumnNumber = sfh.GetColumnNumber(skipFrames);
}
}
}
public StackTrace.ctor()
{
this.m_iNumOfFrames = 0;
this.m_iMethodsToSkip = 0;
this.CaptureStackTrace(0, false, null, null);
}
private void StackTrace.CaptureStackTrace(int iSkip, bool fNeedFileInfo, Thread targetThread, Exception e)
{
this.m_iMethodsToSkip += iSkip;
StackFrameHelper sfh = new StackFrameHelper(fNeedFileInfo, targetThread);
GetStackFramesInternal(sfh, 0, e);
this.m_iNumOfFrames = sfh.GetNumberOfFrames();
if (this.m_iMethodsToSkip > this.m_iNumOfFrames)
{
this.m_iMethodsToSkip = this.m_iNumOfFrames;
}
if (this.m_iNumOfFrames != 0)
{
this.frames = new StackFrame[this.m_iNumOfFrames];
for (int i = 0; i < this.m_iNumOfFrames; i++)
{
bool flag = true;
bool flag2 = true;
StackFrame frame = new StackFrame(flag, flag2);
frame.SetMethodBase(sfh.GetMethodBase(i));
frame.SetOffset(sfh.GetOffset(i));
frame.SetILOffset(sfh.GetILOffset(i));
frame.SetIsLastFrameFromForeignExceptionStackTrace(sfh.IsLastFrameFromForeignExceptionStackTrace(i));
if (fNeedFileInfo)
{
frame.SetFileName(sfh.GetFilename(i));
frame.SetLineNumber(sfh.GetLineNumber(i));
frame.SetColumnNumber(sfh.GetColumnNumber(i));
}
this.frames[i] = frame;
}
if (e == null)
{
this.m_iMethodsToSkip += CalculateFramesToSkip(sfh, this.m_iNumOfFrames);
}
this.m_iNumOfFrames -= this.m_iMethodsToSkip;
if (this.m_iNumOfFrames < 0)
{
this.m_iNumOfFrames = 0;
}
}
else
{
this.frames = null;
}
}
public virtual StackFrame StackTrace.GetFrame(int index)
{
if (((this.frames != null) && (index < this.m_iNumOfFrames)) && (index >= 0))
{
return this.frames[index + this.m_iMethodsToSkip];
}
return null;
}
Related
I have a program which builds a very large tree from input data and traverses it, both by recursion. I have tested the program on smaller inputs (and thus smaller trees) and it functions as intended. However when the input data is much larger i run into 'Process is terminated due to StackOverflowException'. I assume this is due to the stack running out of space. Is there any way to prevent this or do I have to switch to building the tree via iteration instead? Or perhaps I am missing a case of infinite recursion somewhere?
Here is the code:
class Program
{
static int[] tileColors;
static Color[] colors;
static int totalTiles;
static void Main(string[] args)
{
Stopwatch s = new Stopwatch();
s.Start();
string[] data = File.ReadAllLines("colors.txt");
totalTiles = int.Parse(data[0].Split(' ')[0]);
int totalColors = int.Parse(data[0].Split(' ')[1]);
string[] colorsRaw = data[1].Split(' ');
tileColors = new int[totalTiles];
for (int i = 0; i < totalTiles; i++)
{
tileColors[i] = int.Parse(colorsRaw[i]) - 1;
}
colors = new Color[totalColors];
for (int i = 3; i < data.Length; i++)
{
string[] raw = data[i].Split(' ');
int[] pair = new int[] { int.Parse(raw[0]) - 1, int.Parse(raw[1]) - 1 };
if (colors[pair[0]] == null)
colors[pair[0]] = new Color(pair[1]);
else
colors[pair[0]].pairs.Add(pair[1]);
if (colors[pair[1]] == null)
colors[pair[1]] = new Color(pair[0]);
else
colors[pair[1]].pairs.Add(pair[0]);
}
Tree t = new Tree();
t.root = new Node(0);
PopulateTree(t.root);
long ans = t.CountMatchingLeaves(t.root, totalTiles - 1) % 1000000007;
Console.WriteLine(ans);
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
}
static void PopulateTree(Node root)
{
for (int i = root.tile + 1; i < totalTiles; i++)
{
if (colors[tileColors[i]] == null) continue;
if (colors[tileColors[i]].Compatible(tileColors[root.tile]))
{
var node = new Node(i);
root.children.Add(node);
PopulateTree(node);
}
}
}
}
class Color
{
public List<int> pairs = new List<int>();
public Color(int pair)
{
pairs.Add(pair);
}
public bool Compatible(int c)
{
return pairs.Contains(c);
}
}
class Node
{
public List<Node> children = new List<Node>();
public int tile;
public Node(int tile)
{
this.tile = tile;
}
}
class Tree
{
public Node root;
public List<Node> GetMatchingLeaves(Node root, int match)
{
if (root.children.Count == 0)
{
if (root.tile == match)
{
return new List<Node>() { root };
}
return new List<Node>();
}
List<Node> list = new List<Node>();
foreach(var c in root.children)
{
list.AddRange(GetMatchingLeaves(c, match));
}
return list;
}
public long CountMatchingLeaves(Node root, int match)
{
if (root.children.Count == 0)
{
if (root.tile == match)
{
return 1;
}
return 0;
}
long count = 0;
foreach (var c in root.children)
{
count += CountMatchingLeaves(c, match);
}
return count;
}
}
You can always rewrite recursion as iteration, usually by using a stack class rather than rely on your thread's stack. For your code it would look like this:
static void PopulateTree(Node start)
{
var nodes = new Stack<Node>();
nodes.Push(start);
while(nodes.Count != 0)
{
var root = nodes.Pop();
for (int i = root.tile + 1; i < totalTiles; i++)
{
if (colors[tileColors[i]] == null) continue;
if (colors[tileColors[i]].Compatible(tileColors[root.tile]))
{
var node = new Node(i);
root.children.Add(node);
nodes.Push(node);
}
}
}
}
The while loop checking for more items is the equivalent of your terminating condition in recursion.
I have the following code:
void Selection()
{
bool solutionFound = false;
int generation = 0;
int distance = int.MaxValue;
while(!solutionFound)
{
generation++;
for (int i = 0; i < population.Length; i++)
{
population[i].CalcFitness(target);
}
matingPool = new List<DNA>();
for (int i = 0; i < population.Length; i++)
{
int n = (int)(population[i].fitness * 100);
for (int j = 0; j < n; j++)
{
matingPool.Add(population[i]);
}
}
for (int i = 0; i < population.Length; i++)
{
int a = StaticRandom.Instance.Next(matingPool.Count);
int b = StaticRandom.Instance.Next(matingPool.Count);
DNA partnerA = matingPool[a];
DNA partnerB = matingPool[b];
if(partnerA.GetPhrase().Equals(partnerB.GetPhrase()))
{
while(partnerA.GetPhrase().Equals(partnerB.GetPhrase()))
{
a = StaticRandom.Instance.Next(matingPool.Count);
b = StaticRandom.Instance.Next(matingPool.Count);
partnerA = matingPool[a];
partnerB = matingPool[b];
}
}
DNA child = partnerA.Crossover(partnerB);
child.Mutate(mutationRate);
population[i] = child;
ThreadHelperClass.SetText(this, display_lbl, i + ": " + child.GetPhrase());
ThreadHelperClass.SetText(this, gen_lbl, "Generations: " + generation);
int compute = LevenshteinDistance.Compute(target, child.GetPhrase());
if(compute < distance)
{
distance = compute;
ThreadHelperClass.SetText(this, bestPhrase_lbl, "Best Phrase: " + child.GetPhrase());
}
if(child.GetPhrase().Equals(target))
{
solutionFound = true;
break;
}
}
}
}
and the following code
public static class ThreadHelperClass
{
delegate void SetTextCallback(Form f, Control ctrl, string t);
public static void SetText(Form form, Control control, string text)
{
//Invoke requires compares the thread ID of the calling thread
//to the thread ID of the creating thread
//If they are different InvokeRequired sets to true
if (control.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
form.Invoke(d, new Object[] { form, control, text });
}
else
control.Text = text;
}
}
I send Selection to another thread using:
Task.Factory.StartNew(() => Selection(), cToken.Token);
I've already tried to set the ThreadHelper class to use BeginInvoke instead of Invoke and it locks up my UI on the first iteration of the Selection method.
I've also tried to end the task using cToken.Cancel(). This still locks up my UI when the user clicks the stop_btn.
Is there a way I can stop this from happening?
I am trying to make a Genetic Algorithm implementation for my thesis. There are two main class: Facility as chromosome and FacilityCell as gene. But I am getting an error while getting the fitness value from Facility class.
The necessary values are set in the Form.cs and after the algorithm has been run, these properties are null in the Facility instance. These properties are Facility.Flows and Facility.Demands. I can't understand why. Please help.
Code part from Form.cs
fac = new Facility();
List<FacilityCell> gens = new List<FacilityCell>();
for (int i = 0; i < 6; i++)
{
gens.Add(new FacilityCell(i.ToString(), i));
}
fac.Genes = gens.ToArray();
fac.Cells = gens.ToArray();
float[] dems = new float[3];
dems[0] = 300;
dems[1] = 60;
dems[2] = 160;
fac.Demands = dems;
FacilityCell[][] fl = new FacilityCell[3][];
fl[0] = new FacilityCell[] {
fac.Cells[0],
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[3],
fac.Cells[5] };
fl[1] = new FacilityCell[] {
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[5],
fac.Cells[3],
fac.Cells[4] };
fl[2] = new FacilityCell[] {
fac.Cells[1],
fac.Cells[0],
fac.Cells[4],
fac.Cells[2],
fac.Cells[3],
fac.Cells[5] };
fac.Flows = fl;
Code from Facility.cs:
public class Facility : IChromosome
{
public Facility()
{
}
public Facility(FacilityCell[] cells)
{
this.cells = cells;
flows = null;
demands = null;
for (int i = 0; i < cells.Length; i++)
{
cells[i].Order = i;
}
}
private IGene[] cells;
private float[] demands;
private FacilityCell[][] flows;
public FacilityCell[][] Flows
{
get { return flows; }
set { flows = value; }
}
public FacilityCell[] Cells
{
get
{
return cells as FacilityCell[];
}
set
{
cells = value;
}
}
public float[] Demands
{
get { return demands; }
set { demands = value; }
}
public float FitValue
{
get
{
float total = 0;
//I AM GETTING ERROR IN THIS LINE OF CODE, THE FOR LOOP
//It throws NullReferenceException for both this.Demands and this.Flows
for (int i = 0; i < flows.Length; i++)
{
for (int j = 0; j < flows[i].Length - 1; j++)
{
int dist = Math.Abs(flows[i][j + 1].Order - flows[i][j].Order);
float totflow = dist * demands[i];
total += totflow;
}
}
return total;
}
}
public IGene[] Genes
{
get
{
return cells;
}
set
{
cells = value;
}
}
}
This code: FacilityCell[][] fl = new FacilityCell[3][]; will in the constructor set demands to null, You call ths code AFTER you set the demands.
I have written a id manager for a xml repository. The repository manages entries in the xml file and assigns unique id (integers) to each entry that is added. The same way databases auto assign new id's to entries added to a table.
The repository will be called asynchronously so I need the id manager to be thread safe. I am using the C# lock statement but it does not seem to help. My unit tests succeed in single threaded execution but fail when run in parallel ( IE: Task ). Specifically they only fail with large sets of parallel tasks above 1000+ and even only then they only fail every other time.
The exception states that it expected 10000 but got 9998. The exception is always the same having to do with 2 missing id that were not registered.
What the heck am I missing?
ID Manager code and unit tests are provided below. The id manager utilizes Linq and is thus not very performance oriented with large sets of id's. Unit Tests TestAsyncRegistration and TestAsyncRandomRegistration are the tests that throw the exceptions.
public class IdManager
{
private List<int> idList = new List<int>();
private List<int> availableList = new List<int>();
private int nextId;
private int bufferCount;
object obj = new object();
public ReadOnlyCollection<int> RegisteredIds
{
get
{
return new ReadOnlyCollection<int>(this.idList);
}
}
public int BufferCount
{
get
{
return this.bufferCount;
}
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("value");
}
this.bufferCount = value;
}
}
public IdManager(int bufferCount)
{
this.BufferCount = bufferCount;
this.Reset();
}
public IdManager()
: this(1000)
{
}
public void RegisterId(int id)
{
this.RegisterId(new[] { id });
}
public void Reset()
{
lock (this.obj)
{
this.availableList.Clear();
this.idList.Clear();
for (var i = 0; i < this.bufferCount; i++)
{
this.availableList.Add(i);
}
}
}
public void RegisterId(IEnumerable<int> ids)
{
lock (this.obj)
{
var distinct = ids.Except(this.idList);
this.idList.AddRange(distinct);
this.availableList = this.availableList.Except(this.idList).ToList();
}
}
public int NewId()
{
lock (this.obj)
{
if (this.availableList.Count > 0)
{
var item = this.availableList[0];
this.availableList.RemoveAt(0);
this.idList.Add(item);
return item;
}
var max = this.idList.Max();
for (var i = 1; i < this.bufferCount; i++)
{
this.availableList.Add(max + i);
}
this.availableList = this.availableList.Except(this.idList).ToList();
return this.NewId();
}
}
}
... and the unit test code ...
[TestClass]
public class IdManagerTests
{
[TestMethod]
public void TestSequence()
{
var manager = new IdManager(5);
for (var i = 0; i < manager.BufferCount + 10; i++)
{
Assert.AreEqual(i, manager.NewId());
}
}
[TestMethod]
public void TestBrokenSequence()
{
var manager = new IdManager(5);
manager.RegisterId(1);
Assert.AreEqual(0, manager.NewId());
Assert.AreEqual(2, manager.NewId());
for (var i = 3; i < manager.BufferCount + 10; i++)
{
Assert.AreEqual(i, manager.NewId());
}
}
[TestMethod]
public void TestForwardSequence()
{
var manager = new IdManager(5);
manager.RegisterId(0);
manager.RegisterId(1);
manager.RegisterId(2);
Assert.AreEqual(3, manager.NewId());
Assert.AreEqual(4, manager.NewId());
for (var i = 5; i < manager.BufferCount + 10; i++)
{
Assert.AreEqual(i, manager.NewId());
}
}
[TestMethod]
public void TestBackwardSequence()
{
var manager = new IdManager(5);
manager.RegisterId(2);
manager.RegisterId(1);
manager.RegisterId(0);
Assert.AreEqual(3, manager.NewId());
Assert.AreEqual(4, manager.NewId());
for (var i = 5; i < manager.BufferCount + 10; i++)
{
Assert.AreEqual(i, manager.NewId());
}
}
[TestMethod]
public async Task TestLargeNumbersRegistration()
{
// register a list of id's from 0 to 1000
var list = new List<int>();
for (int i = 0; i < 1000; i++)
{
list.Add(i);
}
var manager = new IdManager(1000);
manager.RegisterId(list);
var taskCount = 10000;
var taskList = new Task[taskCount];
var idValue = 0;
for (int i = 0; i < taskList.Length; i++)
{
manager.RegisterId(idValue++);
}
Assert.AreEqual(taskCount, manager.NewId());
}
[TestMethod]
public async Task TestAsyncRegistration()
{
// register a list of id's from 0 to 1000
var list = new List<int>();
for (int i = 0; i < 1000; i++)
{
list.Add(i);
}
var manager = new IdManager(1000);
manager.RegisterId(list);
var taskCount = 10000;
var taskList = new Task[taskCount];
var idValue = 0;
for (int i = 0; i < taskList.Length; i++)
{
taskList[i] = Task.Factory.StartNew(() => manager.RegisterId(idValue++));
}
Task.WaitAll(taskList);
Assert.AreEqual(taskCount, manager.NewId());
}
[TestMethod]
public async Task TestAsyncRandomRegistration()
{
// register a list of id's from 0 to 1000
var list = new List<int>();
for (int i = 0; i < 1000; i++)
{
list.Add(i);
}
// randomize the order of the id's in the list
var random = new Random((int)DateTime.Now.Ticks);
var randomizedList = from item in list
orderby random.Next()
select item;
var manager = new IdManager(1000);
manager.RegisterId(randomizedList);
var taskCount = 10000;
var taskList = new Task[taskCount];
var idValue = 0;
for (int i = 0; i < taskList.Length; i++)
{
taskList[i] = Task.Factory.StartNew(() => manager.RegisterId(idValue++));
}
Task.WaitAll(taskList);
Assert.AreEqual(taskCount, manager.NewId());
}
}
Your problem is in your test, not the method that you're testing, specifically the snippet:
Task.Factory.StartNew(() => manager.RegisterId(idValue++));
You're calling idValue++ from a bunch of different threads simultaneously. That's not a safe operation to perform. Either increment idValue outside of StartNew and pass in the already incremented value, or use Interlocked.Increment to handle it safely.
I am trying to create a program which can sort the number of results associated with any specified google search. I need a big table very fast so I thought about using a loop. Each time I try it though, the debugger crashes due to a "System.Windows.Markup.XamlParseException".
public long resultStat(string a)
{
var req = (HttpWebRequest)WebRequest.Create("https://www.google.ca/search?hl=fr&output=search&sclient=psy-ab&q=a" + a + "&btnK=");
using (req as IDisposable)
{
WebResponse rep = req.GetResponse();
Stream str = rep.GetResponseStream();
StreamReader rdr = new StreamReader(str);
string res = rdr.ReadToEnd();
rdr.Close();
//This is my code to get the number results (it works perfectly)
int index = res.IndexOf(">Environ");
int cond = 0;
string final = "";
try
{
while (res[++index] != '<')
{
if (cond-- == 0 && res[index] != '&')
{ final += res[index]; cond = 0; }
else if (res[index] == '&') cond = 5;
}
}
catch { return 0; }
string temp = "";
foreach (char i in final) if (i < 48 && i > 58) temp += i;
return Int64.Parse(temp);
}
}
This whole method is simply used in the main in a for loop such as :
public void main()
{
//Other code
for (int i = 0; i < 3; i++) resultStat(i.ToString()); // For example
//Other code
}
I know it's the problem because as soon as I comment the loop, or lower it to one rep, nothing goes wrong. I've tried:
HttpWebRequest().Abort();
HttpWebRequest().KeepAlive = false;
It didn't work
I don't think the away you are doing is the correct way to do this. The simple one i can tell you is use Lib curl c#. You can send in an array of urls and get response as an array. That would be perfect for what you require here. Here is a sample class code below that does the multitasking itself. You just send in the urls.
public class MultiHttp
{
public static string UserAgent = "Mozilla 5.0";
public static string Header = "Content-Type: application/x-www-form-urlencoded; charset=UTF-8";
private static string[] Result;
public static string[] MultiPost(string[] Url, string post, int timeOut)
{
Result = new string[post.Length];
try
{
Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);
Easy.WriteFunction wf = new Easy.WriteFunction(OnWriteData);
//Easy.HeaderFunction hf = new Easy.HeaderFunction(OnHeaderData);
Easy[] easy = new Easy[Url.Length];
Multi multi = new Multi();
for (int i = 0; i < Url.Length; i++)
{
if (Url[i] != null)
{
easy[i] = new Easy();
easy[i].SetOpt(CURLoption.CURLOPT_URL, Url[i]);
easy[i].SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf);
easy[i].SetOpt(CURLoption.CURLOPT_WRITEDATA, i);
//easy[i].SetOpt(CURLoption.CURLOPT_HEADERFUNCTION, hf);
//easy[i].SetOpt(CURLoption.CURLOPT_HEADERDATA, i);
easy[i].SetOpt(CURLoption.CURLOPT_TIMEOUT, timeOut);
easy[i].SetOpt(CURLoption.CURLOPT_USERAGENT, UserAgent);
Slist sl = new Slist();
sl.Append(Header);
easy[i].SetOpt(CURLoption.CURLOPT_HTTPHEADER, sl);
easy[i].SetOpt(CURLoption.CURLOPT_POSTFIELDS, post);
easy[i].SetOpt(CURLoption.CURLOPT_FOLLOWLOCATION, true);
easy[i].SetOpt(CURLoption.CURLOPT_POST, true);
//easy[i].SetOpt(CURLoption.CURLOPT_NOBODY, true);
if (Url[i].Contains("https"))
{
easy[i].SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, 1);
easy[i].SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, 0);
}
multi.AddHandle(easy[i]);
}
}
int stillRunning = 1;
while (multi.Perform(ref stillRunning) == CURLMcode.CURLM_CALL_MULTI_PERFORM) ;
while (stillRunning != 0)
{
multi.FDSet();
int rc = multi.Select(1000); // one second
switch (rc)
{
case -1:
stillRunning = 0;
break;
case 0:
default:
{
while (multi.Perform(ref stillRunning) == CURLMcode.CURLM_CALL_MULTI_PERFORM) ;
break;
}
}
}
// various cleanups
multi.Cleanup();
for (int i = 0; i < easy.Length; i++)
{
easy[i].Cleanup();
}
Curl.GlobalCleanup();
}
catch (Exception)
{
//r = ex+"";
}
return Result;
}
public static Int32 OnWriteData(Byte[] buf, Int32 size, Int32 nmemb,
Object extraData)
{
int tmp = Convert.ToInt32(extraData.ToString()); ;
Result[tmp] += System.Text.Encoding.UTF8.GetString(buf);
return size * nmemb;
}
}
Call it like :
String[] url= new String[2];
url[1]="https://www.google.ca/search?hl=fr&output=search&sclient=psy-ab&q=a1&btnK=";
url[2]="https://www.google.ca/search?hl=fr&output=search&sclient=psy-ab&q=a2&btnK=";
string postString=""; // IF YOU DO NOT WANT TO POST ANYTHING YOU CAN DO THE SAME THING KEEP URL THE SAME SEND POST ARRAY AND CHANGE THE CLASS IT WORKS BOTH WAYS
String[] result = MultiHttp.MultiPost(url, postString, timeOut);
Its just a sample but will get you the working idea to sort out your problem.