C++ Multithreading Technical Most Frequently Asked Interview Questions Answers
What Header File Should You Include For Using C++11 Multithreading Capabilities?
Use the <thread> header file
#include <thread>
Note: The thread functionality is defined in the "std" namespace.
How Can A C++11 Thread Be Created With A Member Function?
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
class Torpedo
{
public:
void LaunchTorpedo()
{
cout << " Launching Torpedo" << endl;
}
};
int main()
{
//Execute the LaunchTorpedo() method for a specific Torpedo object on a seperate thread
Torpedo torpedo;
thread t1(&Torpedo::LaunchTorpedo, &torpedo);
t1.join();
return 0;
}
Note that here you're executing the LaunchTorpedo() method for a specific Torpedo object on a seperate thread. If other threads are accessing the same "torpedo" object, you'll need to protect the shared resources of that object with a mutex.
What Does Joining C++11 Threads Mean? Alternatively What Does The Std::thread::join() Do?
A call to std::thread::join() blocks until the thread on which join is called, has finished executing. In each of the examples above, the join() call ensures that the main method waits for the execution of the spawned threads to finish before it can exit the application.
On the other hand, if we do not call join() after creating a thread in the above case, the main function will not wait for the spawned thread to complete before it tears down the application. If the application tears down before the spawned thread finishes, it will terminate the spawned thread as well, even if it has not finished executing. This can leave data in a very inconsistent state and should be avoided at all cost.
Can You Name A Situation Where Joining Threads Should Be Avoided?
A call to join() blocks the caller thread. This is really bad in situations where the caller thread is a main UI thread – because if the UI thread blocks, the application will stop responding to user inputs which will make it seem hanged.
Another place where calling join() is not advisable is inside a main game loop. Calling join() can block update and rendering of the game scene and severly impact the user experience (it'll be like watching a You tube video on a dial up internet connection !).
Can You Create A C++11 Thread With A Function Pointer That Takes A Bunch Of Arguments?
Yes ! You can just pass the function arguments to the thread constructor. The thread constructor is a variadic template, which means it can accept any number of arguments. Here's an example:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
using namespace std;
void FireTorpedo(int numCities, string torpedoType)
{
cout << "Firing torpedo " << torpedoType << " at" << numCities << " cities." << endl;
}
int main()
{
thread t1(FireTorpedo, 3, "HungryShark");
t1.join();
return 0;
}
What Are The Different Ways Of Creating A Thread In C++11?
There are essentially four ways of creating a thread:
Create a thread with a function pointer
Create a thread with a function object
Create a thread with a lambda
Create a thread with a member function
How Can A C++11 Thread Be Created With A Function Pointer?
Just pass in the address of a function to the thread constructor. The thread will start executing the function immediately.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
void FireMissile()
{
cout << "Firing sidewinder missile " << endl;
}
int main()
{
//Creating a thread with a function pointer
thread t1(FireMissile);
t1.join();
return 0;
}
How Can A C++11 Thread Be Created With A Function Object?
Create a function object "Missile" and pass it to the thread constructor.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
//Create the function object
class Missile
{
public:
void operator() () const
{
cout << "Firing Tomahawk missile" << endl;
}
};
int main()
{
//Creating a thread with an function object
Missile tomahawk;
thread t1(tomahawk);
t1.join();
return 0;
}
How Can A C++11 Thread Be Created With A Lambda?
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
int main()
{
thread t1([] {
cout << "Launching Scud missile" << endl;
});
t1.join();
return 0;
}
Can A Lambda Closure Be Used To Create A C++11 Thread?
Yes ! A lambda closure is nothing but a variable storing a lambda expression. You can store a lambda in a closure if you intend to reuse the lambda expression at more than one place in your code.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
int main()
{
// Define a lambda closure
auto LaunchMissileFunc = []() -> void { cout << "Launching Cruiser Missile" << endl; };
thread t1(LaunchMissileFunc);
t1.join();
return 0;
}
Can You Create A C++11 Thread With A Lambda Closure That Takes A Bunch Of Arguments?
Yes – just like the previous case, you can pass the arguments needed by the lambda closure to the thread constructor.
auto LaunchTorpedoFunc = [](int numCities, string torpedoType) -> void { cout << "Firing torpedo " << torpedoType << " at" << numCities << " cities." << endl; };
thread t1(LaunchTorpedoFunc, 7, "Barracuda");
t1.join();
Are The Arguments Passed To A C++11 Thread's Constructor Pass By Vale Or Pass By Reference?
Thread function arguments are always pass by value, i.e., they are always copied into the internal storage for threads. Any changes made by the thread to the arguments passed does not affect the original arguments. For example, we want the "targetCity" to be modified by the thread but it never happens:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void ChangeCurrentMissileTarget(string& targetCity)
{
targetCity = "Metropolis";
cout << " Changing The Target City To " << targetCity << endl;
}
int main()
{
string targetCity = "Star City";
thread t1(ChangeCurrentMissileTarget, targetCity);
t1.join();
cout << "Current Target City is " << targetCity << endl;
return 0;
}
OUTPUT:
Changing The Target City To Metropolis
Current Target City is Star City
Note that the "targetCity" variable is not modified.
How Can We Pass C++11 Thread Arguments By Reference?
We need to use std::ref() from the <functional> header. Consider the following code snippet and associated output.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void ChangeCurrentMissileTarget(string& targetCity)
{
targetCity = "Metropolis";
cout << " Changing The Target City To " << targetCity << endl;
}
int main()
{
string targetCity = "Star City";
thread t1(ChangeCurrentMissileTarget, std::ref(targetCity));
t1.join();
cout << "Current Target City is " << targetCity << endl;
return 0;
}
OUTPUT:
Changing The Target City To Metropolis
Current Target City is Metropolis
Notice that the changes to "targetCity" made by the thread was preserved once the thread exited.
What Is C++11 Thread Local Storage (thread_local)?
A thread_local object comes into existence when a thread starts and is destroyed when the thread ends. Each thread has its own instance of a thread-Local object.
To fully understand the implications, let's look at an example- here we'll declare a global variable "globalvar" as thread_local. This'll give each thread it's own copy of globalVar and any modifications made to globalVar will only persist inside that particular thread.In the example below, each of the two threads are modifying globalVar – but they are not seeing each other's change, neither is the main thread.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
#include <mutex>
using namespace std;
thread_local int globalVar = 0;
mutex mu;
void PrettyPrint(int valueToPrint)
{
lock_guard<mutex> lock(mu);
cout << "Value of globalVar in thread " << this_thread::get_id() << " is " << globalVar << endl;
}
void thread_Local_Test_Func(int newVal)
{
globalVar = newVal;
PrettyPrint(globalVar);
}
int main()
{
globalVar = 1;
thread t1(thread_Local_Test_Func, 5);
thread t2(thread_Local_Test_Func, 20);
t1.join();
t2.join();
cout << "Value of globalVar in MAIN thread is " << globalVar << endl;
return 0;
}
Here's the output of the program – you can see that the three threads (t1, t2 and MAIN) does not see each other's changes to globalVar.
Value of globalVar in thread 17852 is 5
Value of globalVar in thread 29792 is 20
Value of globalVar in MAIN thread is 1
Can you guess what the output will be if globalVar was not declared thread_local ? Here it is :
Value of globalVar in thread 27200 is 5
Value of globalVar in thread 31312 is 20
Value of globalVar in MAIN thread is 20
If the global value was not thread local, the change made by each thread will be persisted outside the thread – here the MAIN thread is feeling the effect of the change made by t2 and hence printing "20" instead of "1".
How Can You Retrieve Results From A Thread?
As we'll see in a subsequent tutorial, the easiest and recommended way is to use "futures". However, you can still get the result of some calculation from a thread by either:
Passing reference to a result variable to the thread in which the thread stores the results
Store the result inside a class memeber variable of a function object which can be retrieved once the thread has finished executing.
What Is "oversubscription"?
Oversubscription is a situation where more threads are vying for run-time than the underlying hardware can support. One of the biggest cost associated with multiple threads is that of context-switches that happens when the processor switches threads. Ideally, the you'd not want to create more threads than the hardware can support.
How Can I Avoid "oversubscription" In C++11 When Working With Multiple Threads?
C++11 provides a way to get a hint at the number of threads that can be run in parallel from an application – which most of the time coincides with the number of logical cores.
unsigned int n = std::thread::hardware_concurrency();
On my system with 12 logical cores, it returns 12. This means that I should not attempt to fork more than 12 threads in my application. Note that this is VC++ – other C++ compiler implementations might give different results
How Can You Identify Different C++11 Threads?
C++11 gives unique ids to forked threads which can be retrieved using :
By calling the get_id() member function for a specific thread
By calling std::this_thread::get_id() for the currently executing thread
An example of both is given below:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void Count()
{
for (int i = 0; i < 100; i++)
{
cout << "counter at: " << i << endl;
}
}
int main()
{
thread t22(Count);
//Get the ID of the t22 thread
std::thread::id k = t22.get_id();
cout << k << endl;
//Get the ID of the MAIN Thread
std::thread::id j = std::this_thread::get_id();
cout << j << endl;
return 0;
}
If I run this code, I can see the thread ids in "threads" and "locals" window. Also note that the thread name is almost useless.
However, the "Location" column can give an indication as to which thread is executing.
Does A C++11 Thread Act On A Specific Instance Of A Function Object?
No – function objects are copied to the internal storage for the thread. If you need to execute the operation on a specific instance of the function object, you should use std::ref() from <functional> header to pass your function object by reference.
How Can You Create Background Tasks With C++11 Threads?
You can make a std::thread run in the background by calling std::thread::detach() on it. Once detached, a thread continues to run in the background and cannot be communicated with or waited upon to complete. When you detach a thread, the ownership and control passes over to the C++ Runtime Library, which ensures that the resources allocated to the thread are deallocated once the thread exits.
Here's a contrived example. We have a Count() function that prints numbers 1 to 1000 on the screen. If we create a thread to run the function and detach the thread immediately, we'll not see any output – because the main thread terminates before the "Count" thread has had an opportunity to run. To see some of the output, we can put the main thread to sleep for 10 miliseconds which gives the "count" thread to send some of the output to the screen.
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
void Count()
{
for (int i = 0; i < 100; i++)
{
cout << "counter at: " << i << endl;
}
}
int main()
{
thread t1(Count);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
t1.detach();
return 0;
}
Can The Ownership Of C++11 Threads Be Transferred At Runtime?
Yes. std::thread object owns a resource, where the resource is a current thread of execution. You can call std::move to move the ownership of the underlying resource from one std::thread object to another. The question is – why would you want to do that? Here's a scenario:You want to write a function that creates a thread but does not want to wait for it to finish. Instead it wants to pass the thread to another function which will wait for the thread to finish and execute some action once the execution is done.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void FireHTTPGet()
{
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
cout << "Finished Executing HTTP Get"<< endl;
}
void ProcessHTTPResult(thread t1)
{
t1.join();
cout << "HTTP Get Thread Finished Executing - Processing Result Data!" << endl;
}
int main()
{
thread t11(FireHTTPGet);
thread t12(ProcessHTTPResult, std::move(t11));
//Do bunch of other processing without waiting for t11 to finish - instead now we've shouldered off the
// responsibility of monitoring t11 thread to t12.
//Finally wait for t12 to finish
t12.join();
return 0;
}
OUTPUT:
Finished Executing HTTP Get
HTTP Get Thread Finished Executing - Processing Result Data!
What Header File Should You Include For Using C++11 Multithreading Capabilities?
Use the <thread> header file
#include <thread>
Note: The thread functionality is defined in the "std" namespace.
How Can A C++11 Thread Be Created With A Member Function?
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
class Torpedo
{
public:
void LaunchTorpedo()
{
cout << " Launching Torpedo" << endl;
}
};
int main()
{
//Execute the LaunchTorpedo() method for a specific Torpedo object on a seperate thread
Torpedo torpedo;
thread t1(&Torpedo::LaunchTorpedo, &torpedo);
t1.join();
return 0;
}
Note that here you're executing the LaunchTorpedo() method for a specific Torpedo object on a seperate thread. If other threads are accessing the same "torpedo" object, you'll need to protect the shared resources of that object with a mutex.
C++ Multithreading Technical Most Frequently Asked Interview Questions Answers |
What Does Joining C++11 Threads Mean? Alternatively What Does The Std::thread::join() Do?
A call to std::thread::join() blocks until the thread on which join is called, has finished executing. In each of the examples above, the join() call ensures that the main method waits for the execution of the spawned threads to finish before it can exit the application.
On the other hand, if we do not call join() after creating a thread in the above case, the main function will not wait for the spawned thread to complete before it tears down the application. If the application tears down before the spawned thread finishes, it will terminate the spawned thread as well, even if it has not finished executing. This can leave data in a very inconsistent state and should be avoided at all cost.
Can You Name A Situation Where Joining Threads Should Be Avoided?
A call to join() blocks the caller thread. This is really bad in situations where the caller thread is a main UI thread – because if the UI thread blocks, the application will stop responding to user inputs which will make it seem hanged.
Another place where calling join() is not advisable is inside a main game loop. Calling join() can block update and rendering of the game scene and severly impact the user experience (it'll be like watching a You tube video on a dial up internet connection !).
Can You Create A C++11 Thread With A Function Pointer That Takes A Bunch Of Arguments?
Yes ! You can just pass the function arguments to the thread constructor. The thread constructor is a variadic template, which means it can accept any number of arguments. Here's an example:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
using namespace std;
void FireTorpedo(int numCities, string torpedoType)
{
cout << "Firing torpedo " << torpedoType << " at" << numCities << " cities." << endl;
}
int main()
{
thread t1(FireTorpedo, 3, "HungryShark");
t1.join();
return 0;
}
What Are The Different Ways Of Creating A Thread In C++11?
There are essentially four ways of creating a thread:
Create a thread with a function pointer
Create a thread with a function object
Create a thread with a lambda
Create a thread with a member function
How Can A C++11 Thread Be Created With A Function Pointer?
Just pass in the address of a function to the thread constructor. The thread will start executing the function immediately.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
void FireMissile()
{
cout << "Firing sidewinder missile " << endl;
}
int main()
{
//Creating a thread with a function pointer
thread t1(FireMissile);
t1.join();
return 0;
}
How Can A C++11 Thread Be Created With A Function Object?
Create a function object "Missile" and pass it to the thread constructor.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
//Create the function object
class Missile
{
public:
void operator() () const
{
cout << "Firing Tomahawk missile" << endl;
}
};
int main()
{
//Creating a thread with an function object
Missile tomahawk;
thread t1(tomahawk);
t1.join();
return 0;
}
How Can A C++11 Thread Be Created With A Lambda?
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
int main()
{
thread t1([] {
cout << "Launching Scud missile" << endl;
});
t1.join();
return 0;
}
Can A Lambda Closure Be Used To Create A C++11 Thread?
Yes ! A lambda closure is nothing but a variable storing a lambda expression. You can store a lambda in a closure if you intend to reuse the lambda expression at more than one place in your code.
#include "stdafx.h"
#include <thread>
#include <iostream>
using namespace std;
int main()
{
// Define a lambda closure
auto LaunchMissileFunc = []() -> void { cout << "Launching Cruiser Missile" << endl; };
thread t1(LaunchMissileFunc);
t1.join();
return 0;
}
Can You Create A C++11 Thread With A Lambda Closure That Takes A Bunch Of Arguments?
Yes – just like the previous case, you can pass the arguments needed by the lambda closure to the thread constructor.
auto LaunchTorpedoFunc = [](int numCities, string torpedoType) -> void { cout << "Firing torpedo " << torpedoType << " at" << numCities << " cities." << endl; };
thread t1(LaunchTorpedoFunc, 7, "Barracuda");
t1.join();
Are The Arguments Passed To A C++11 Thread's Constructor Pass By Vale Or Pass By Reference?
Thread function arguments are always pass by value, i.e., they are always copied into the internal storage for threads. Any changes made by the thread to the arguments passed does not affect the original arguments. For example, we want the "targetCity" to be modified by the thread but it never happens:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void ChangeCurrentMissileTarget(string& targetCity)
{
targetCity = "Metropolis";
cout << " Changing The Target City To " << targetCity << endl;
}
int main()
{
string targetCity = "Star City";
thread t1(ChangeCurrentMissileTarget, targetCity);
t1.join();
cout << "Current Target City is " << targetCity << endl;
return 0;
}
OUTPUT:
Changing The Target City To Metropolis
Current Target City is Star City
Note that the "targetCity" variable is not modified.
How Can We Pass C++11 Thread Arguments By Reference?
We need to use std::ref() from the <functional> header. Consider the following code snippet and associated output.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void ChangeCurrentMissileTarget(string& targetCity)
{
targetCity = "Metropolis";
cout << " Changing The Target City To " << targetCity << endl;
}
int main()
{
string targetCity = "Star City";
thread t1(ChangeCurrentMissileTarget, std::ref(targetCity));
t1.join();
cout << "Current Target City is " << targetCity << endl;
return 0;
}
OUTPUT:
Changing The Target City To Metropolis
Current Target City is Metropolis
Notice that the changes to "targetCity" made by the thread was preserved once the thread exited.
What Is C++11 Thread Local Storage (thread_local)?
A thread_local object comes into existence when a thread starts and is destroyed when the thread ends. Each thread has its own instance of a thread-Local object.
To fully understand the implications, let's look at an example- here we'll declare a global variable "globalvar" as thread_local. This'll give each thread it's own copy of globalVar and any modifications made to globalVar will only persist inside that particular thread.In the example below, each of the two threads are modifying globalVar – but they are not seeing each other's change, neither is the main thread.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
#include <mutex>
using namespace std;
thread_local int globalVar = 0;
mutex mu;
void PrettyPrint(int valueToPrint)
{
lock_guard<mutex> lock(mu);
cout << "Value of globalVar in thread " << this_thread::get_id() << " is " << globalVar << endl;
}
void thread_Local_Test_Func(int newVal)
{
globalVar = newVal;
PrettyPrint(globalVar);
}
int main()
{
globalVar = 1;
thread t1(thread_Local_Test_Func, 5);
thread t2(thread_Local_Test_Func, 20);
t1.join();
t2.join();
cout << "Value of globalVar in MAIN thread is " << globalVar << endl;
return 0;
}
Here's the output of the program – you can see that the three threads (t1, t2 and MAIN) does not see each other's changes to globalVar.
Value of globalVar in thread 17852 is 5
Value of globalVar in thread 29792 is 20
Value of globalVar in MAIN thread is 1
Can you guess what the output will be if globalVar was not declared thread_local ? Here it is :
Value of globalVar in thread 27200 is 5
Value of globalVar in thread 31312 is 20
Value of globalVar in MAIN thread is 20
If the global value was not thread local, the change made by each thread will be persisted outside the thread – here the MAIN thread is feeling the effect of the change made by t2 and hence printing "20" instead of "1".
How Can You Retrieve Results From A Thread?
As we'll see in a subsequent tutorial, the easiest and recommended way is to use "futures". However, you can still get the result of some calculation from a thread by either:
Passing reference to a result variable to the thread in which the thread stores the results
Store the result inside a class memeber variable of a function object which can be retrieved once the thread has finished executing.
What Is "oversubscription"?
Oversubscription is a situation where more threads are vying for run-time than the underlying hardware can support. One of the biggest cost associated with multiple threads is that of context-switches that happens when the processor switches threads. Ideally, the you'd not want to create more threads than the hardware can support.
How Can I Avoid "oversubscription" In C++11 When Working With Multiple Threads?
C++11 provides a way to get a hint at the number of threads that can be run in parallel from an application – which most of the time coincides with the number of logical cores.
unsigned int n = std::thread::hardware_concurrency();
On my system with 12 logical cores, it returns 12. This means that I should not attempt to fork more than 12 threads in my application. Note that this is VC++ – other C++ compiler implementations might give different results
How Can You Identify Different C++11 Threads?
C++11 gives unique ids to forked threads which can be retrieved using :
By calling the get_id() member function for a specific thread
By calling std::this_thread::get_id() for the currently executing thread
An example of both is given below:
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void Count()
{
for (int i = 0; i < 100; i++)
{
cout << "counter at: " << i << endl;
}
}
int main()
{
thread t22(Count);
//Get the ID of the t22 thread
std::thread::id k = t22.get_id();
cout << k << endl;
//Get the ID of the MAIN Thread
std::thread::id j = std::this_thread::get_id();
cout << j << endl;
return 0;
}
If I run this code, I can see the thread ids in "threads" and "locals" window. Also note that the thread name is almost useless.
However, the "Location" column can give an indication as to which thread is executing.
Does A C++11 Thread Act On A Specific Instance Of A Function Object?
No – function objects are copied to the internal storage for the thread. If you need to execute the operation on a specific instance of the function object, you should use std::ref() from <functional> header to pass your function object by reference.
How Can You Create Background Tasks With C++11 Threads?
You can make a std::thread run in the background by calling std::thread::detach() on it. Once detached, a thread continues to run in the background and cannot be communicated with or waited upon to complete. When you detach a thread, the ownership and control passes over to the C++ Runtime Library, which ensures that the resources allocated to the thread are deallocated once the thread exits.
Here's a contrived example. We have a Count() function that prints numbers 1 to 1000 on the screen. If we create a thread to run the function and detach the thread immediately, we'll not see any output – because the main thread terminates before the "Count" thread has had an opportunity to run. To see some of the output, we can put the main thread to sleep for 10 miliseconds which gives the "count" thread to send some of the output to the screen.
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
void Count()
{
for (int i = 0; i < 100; i++)
{
cout << "counter at: " << i << endl;
}
}
int main()
{
thread t1(Count);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
t1.detach();
return 0;
}
Can The Ownership Of C++11 Threads Be Transferred At Runtime?
Yes. std::thread object owns a resource, where the resource is a current thread of execution. You can call std::move to move the ownership of the underlying resource from one std::thread object to another. The question is – why would you want to do that? Here's a scenario:You want to write a function that creates a thread but does not want to wait for it to finish. Instead it wants to pass the thread to another function which will wait for the thread to finish and execute some action once the execution is done.
#include "stdafx.h"
#include <string>
#include <thread>
#include <iostream>
#include <functional>
using namespace std;
void FireHTTPGet()
{
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
cout << "Finished Executing HTTP Get"<< endl;
}
void ProcessHTTPResult(thread t1)
{
t1.join();
cout << "HTTP Get Thread Finished Executing - Processing Result Data!" << endl;
}
int main()
{
thread t11(FireHTTPGet);
thread t12(ProcessHTTPResult, std::move(t11));
//Do bunch of other processing without waiting for t11 to finish - instead now we've shouldered off the
// responsibility of monitoring t11 thread to t12.
//Finally wait for t12 to finish
t12.join();
return 0;
}
OUTPUT:
Finished Executing HTTP Get
HTTP Get Thread Finished Executing - Processing Result Data!
Post a Comment