Skip to main content
1 of 4
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341

##You have a threading bug here:

void fetch_weather_async()
{
    while (current_city + 1 < (int) cities.size()){
        // Say there is only one city left to fetch the data from.
        // And both threads have reached this lock at the same time
        // (multi processor system). One will get the lock the other
        // will stall waiting for the lock.
        city_counter_lock.lock();

        int city_index = 0;

        // The first thread threw the lock will satisfy this
        // condition and get to processes the last city.
        // The second thread through the lock (after the first
        // releases it) will find this condition will fail and
        // as a result have `city_index = 0`
        if (current_city + 1 < (int) cities.size()) {
            city_index = ++current_city;
        }
        city_counter_lock.unlock();

        // Both threads now execute the following code.  
        // But one is set to processes the last item,
        // the other is going to re-processes the first item.
        std::cout << "requesting " << city_index << std::endl;  
        fetchJson(city_index);
        std::cout << "acquired " << city_index << std::endl; 

        // You have encountered your first race condition.   
    }
}

##Your timing is not accurate:

std::thread thread_1(&fetch_weather_async);
std::thread thread_2(&fetch_weather_async);

// By the time you get the time here.
// The two threads could have actually finished.
// So you are just timing the cost of the join operation.
// --
// Or the threads could be half finished by the time you get
// here. So what you are timing here is indeterminate.
start = std::time(nullptr);
thread_1.join();
thread_2.join();
finish = std::time(nullptr);

##Your biggest bottle neck is here:

    city_counter_lock.lock();
    int city_index = 0;
    if (current_city + 1 < (int) cities.size()) {
        city_index = ++current_city;
    }
    city_counter_lock.unlock();

By simply removing this bottle neck I removed 5 seconds (averaged over three runs). I simply made one thread do even cities and the other thread do odd cities (I know its not a perfect solution but I wanted do demonstrate the cost of threads being blocked by each other on such a coarse grain).

 // I changed the declaration of `fetch_weather_async()`
 void fetch_weather_async(bool);

 // I changed how the the thread is called.
 std::thread thread_1(fetch_weather_async, false);
 std::thread thread_2(fetch_weather_async, true);


// Finally I simply changed the function
// so they started at different points.
void fetch_weather_async(bool odd){
    unsigned int cityIndex = (odd ? 1 : 0);
    for (;cityIndex  < cities.size(); cityIndex += 2){
        std::cout << "requesting " << cityIndex << std::endl;
        fetchJson(cityIndex);
        std::cout << "acquired " << cityIndex << std::endl;
    }
}

##Another Bug (but this one a normal bug)

        // This is a classic anti pattern for reading a file.
        // The trouble is the last read reads upto but not past
        // the EOF. Thus even though there is no data left in the
        // file to read the method eof() will return false.
        // So you will enter the loop even though there is nothing
        // to read.
        while(! idFile.eof()){   


            // The problem is here.
            // If there is nothing left to read in the file this
            // getline will fail. You should be checking the result
            // of calling getline before performing the next action.
            //
            // I am not sure of the affect on `currentLine` when
            // the call fails it is either made blank or left unchanged.
            // So the last line in your list is either empty
            // or a repeat of the last item in the file.
            getline (idFile, currentLine);
            cities.push_back(currentLine);  
        }

The correct way to use a loop to read a file is:

    std::ifstream  file("FileName");
    std::string    line;

    // Notice we check the result of the `getline()` as part of
    // the loop condition. So we only enter the loop body if the
    // read happened correctly.
    while(std::getline(file, line)) {
        cities.push_back(line);
    }

If all your cities are guaranteed to be one word. you can use a simpler trick.

   std::vector<std::string>  cities(std::istream_iterator<std::string>(file),
                                    std::istream_iterator<std::string>()); 
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341